WPF 用户控件上的双向绑定
TwoWay binding on WPF UserControl
我正在尝试构建一个简单的拾色器作为 WPF 用户控件,但我无法将所选颜色返回到主 window。
我的主要 window XAML 看起来像这样:
<Window x:Class="ColorPicker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:ColorPicker"
Title="TestWindow" SizeToContent="WidthAndHeight">
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding MyColor}" Margin="10,10,10,10" Width="100" Height="300"/>
<view:WPFColorPicker SelectedColor="{Binding MyColor, Mode=TwoWay}" Width="200" Height="300"/>
</StackPanel>
</Window>
以及视图模型和代码隐藏:
namespace ColorPicker
{
public class TestViewModel
{
public TestViewModel()
{
MyColor = new SolidColorBrush(Color.FromRgb(255, 0, 0));
}
public Brush MyColor { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
}
WPFColorPicker 用户控件 XAML 是:
<UserControl x:Class="ColorPicker.WPFColorPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Rectangle x:Name="rtlfill" Fill="{Binding SelectedColor}" HorizontalAlignment="Stretch" Height="60" Margin="10,10,10,10" Stroke="Black" VerticalAlignment="Top"/>
<ListBox HorizontalAlignment="Stretch" SelectedValue="{Binding SelectedColor}" VerticalAlignment="Stretch" Margin="0,0,0,81" ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="colorList">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="{Binding .}" Margin="0,0,0,5" Width="20" Height="20" Stroke="#FF211E1E" OpacityMask="Black" StrokeThickness="1" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</UserControl>
和代码隐藏:
namespace ColorPicker
{
public partial class WPFColorPicker : UserControl
{
List<Brush> brushList;
public WPFColorPicker()
{
InitializeComponent();
brushList = new List<Brush>() {
new SolidColorBrush(Color.FromRgb(255, 0, 0)),
new SolidColorBrush(Color.FromRgb( 0,255, 0)),
new SolidColorBrush(Color.FromRgb( 0, 0,255))};
this.colorList.ItemsSource = brushList;
DataContext = this;
}
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor",
typeof(Brush),
typeof(WPFColorPicker),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Red),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public Brush SelectedColor
{
get { return (Brush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
}
}
所以,我遇到的问题是绑定到 SelectedColor(使用 TestViewModel 中的 MyColor)不起作用。
通过查看 Whosebug 上的其他问题和各种教程,我认为 UserControl 设置正确,可以将 SelectedColor 绑定为 TwoWay DependencyProperty,但它不起作用。
有人可以提供一些见解吗?
假设您的 Window 的 DataContext 设置为您的 ViewModel。
您的颜色选择器是 UserControl
,因此绑定如下:
SelectedColor="{Binding MyColor, Mode=TwoWay}"
正在尝试在 UserControl 的 DataContext 中查找 MyColor
,而不是父控件 window。
您可以给 window 一个名称并使用 ElementName 绑定:
SelectedColor="{Binding DataContext.MyColor, ElementName=windowName, Mode=TwoWay}"
或者更好的是,使用相对源绑定:
SelectedColor="{Binding DataContext.MyColor, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay}"
每个 FrameworkElement
只能有一个 DataContext
所以当你这样做时
DataContext = this;
在 UserControl
构造函数中,您覆盖 DataContext
通常通过可视化树继承,这会影响 WPFColorPicker
和所有子项的默认绑定上下文,包括
<view:WPFColorPicker SelectedColor="{Binding MyColor, Mode=TwoWay}" .../>
从 WPFColorPicker
构造函数中删除该行,而是给 UserControl
一些名称
<UserControl x:Class="ColorPicker.WPFColorPicker" ... x:Name="myUserControl">
并更改 UserControl
内的绑定以使用该名称
<Rectangle ... Fill="{Binding ElementName=myUserControl, Path=SelectedColor}"/>
<ListBox ... SelectedValue="{Binding ElementName=myUserControl, Path=SelectedColor}">
编辑
作为旁注,您需要注意 Brush
是通过引用进行比较的,因此 ListBox.SelectedValue
不会预选值,除非它是 brushList
中的实例之一,而这不是每次创建列表时都可能。基本上 SolidColorBrush
的两个不同实例,即使具有相同的 Color
,对于相等性检查
也是不同的
我正在尝试构建一个简单的拾色器作为 WPF 用户控件,但我无法将所选颜色返回到主 window。
我的主要 window XAML 看起来像这样:
<Window x:Class="ColorPicker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:ColorPicker"
Title="TestWindow" SizeToContent="WidthAndHeight">
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding MyColor}" Margin="10,10,10,10" Width="100" Height="300"/>
<view:WPFColorPicker SelectedColor="{Binding MyColor, Mode=TwoWay}" Width="200" Height="300"/>
</StackPanel>
</Window>
以及视图模型和代码隐藏:
namespace ColorPicker
{
public class TestViewModel
{
public TestViewModel()
{
MyColor = new SolidColorBrush(Color.FromRgb(255, 0, 0));
}
public Brush MyColor { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
}
WPFColorPicker 用户控件 XAML 是:
<UserControl x:Class="ColorPicker.WPFColorPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Rectangle x:Name="rtlfill" Fill="{Binding SelectedColor}" HorizontalAlignment="Stretch" Height="60" Margin="10,10,10,10" Stroke="Black" VerticalAlignment="Top"/>
<ListBox HorizontalAlignment="Stretch" SelectedValue="{Binding SelectedColor}" VerticalAlignment="Stretch" Margin="0,0,0,81" ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="colorList">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="{Binding .}" Margin="0,0,0,5" Width="20" Height="20" Stroke="#FF211E1E" OpacityMask="Black" StrokeThickness="1" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</UserControl>
和代码隐藏:
namespace ColorPicker
{
public partial class WPFColorPicker : UserControl
{
List<Brush> brushList;
public WPFColorPicker()
{
InitializeComponent();
brushList = new List<Brush>() {
new SolidColorBrush(Color.FromRgb(255, 0, 0)),
new SolidColorBrush(Color.FromRgb( 0,255, 0)),
new SolidColorBrush(Color.FromRgb( 0, 0,255))};
this.colorList.ItemsSource = brushList;
DataContext = this;
}
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor",
typeof(Brush),
typeof(WPFColorPicker),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Red),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public Brush SelectedColor
{
get { return (Brush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
}
}
所以,我遇到的问题是绑定到 SelectedColor(使用 TestViewModel 中的 MyColor)不起作用。
通过查看 Whosebug 上的其他问题和各种教程,我认为 UserControl 设置正确,可以将 SelectedColor 绑定为 TwoWay DependencyProperty,但它不起作用。
有人可以提供一些见解吗?
假设您的 Window 的 DataContext 设置为您的 ViewModel。
您的颜色选择器是 UserControl
,因此绑定如下:
SelectedColor="{Binding MyColor, Mode=TwoWay}"
正在尝试在 UserControl 的 DataContext 中查找 MyColor
,而不是父控件 window。
您可以给 window 一个名称并使用 ElementName 绑定:
SelectedColor="{Binding DataContext.MyColor, ElementName=windowName, Mode=TwoWay}"
或者更好的是,使用相对源绑定:
SelectedColor="{Binding DataContext.MyColor, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay}"
每个 FrameworkElement
只能有一个 DataContext
所以当你这样做时
DataContext = this;
在 UserControl
构造函数中,您覆盖 DataContext
通常通过可视化树继承,这会影响 WPFColorPicker
和所有子项的默认绑定上下文,包括
<view:WPFColorPicker SelectedColor="{Binding MyColor, Mode=TwoWay}" .../>
从 WPFColorPicker
构造函数中删除该行,而是给 UserControl
一些名称
<UserControl x:Class="ColorPicker.WPFColorPicker" ... x:Name="myUserControl">
并更改 UserControl
内的绑定以使用该名称
<Rectangle ... Fill="{Binding ElementName=myUserControl, Path=SelectedColor}"/>
<ListBox ... SelectedValue="{Binding ElementName=myUserControl, Path=SelectedColor}">
编辑
作为旁注,您需要注意 Brush
是通过引用进行比较的,因此 ListBox.SelectedValue
不会预选值,除非它是 brushList
中的实例之一,而这不是每次创建列表时都可能。基本上 SolidColorBrush
的两个不同实例,即使具有相同的 Color
,对于相等性检查