【问题标题】:TwoWay binding on WPF UserControlWPF UserControl 上的双向绑定
【发布时间】:2015-05-21 06:24:34
【问题描述】:

我正在尝试构建一个简单的颜色选择器作为 WPF 用户控件,但我无法将所选颜色返回到主窗口。

我的主窗口 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)不起作用。

通过查看有关 StackOverflow 和各种教程的其他问题,我认为 UserControl 设置正确以将 SelectedColor 绑定为 TwoWay DependencyProperty,但它不起作用。

谁能给我一些见解?

【问题讨论】:

    标签: c# wpf xaml user-controls


    【解决方案1】:

    每个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,对于相等性检查也是不同的

    【讨论】:

    • 谢谢,我没有意识到我是自己最大的敌人。
    【解决方案2】:

    假设您的 Window 的 DataContext 设置为您的 ViewModel。

    您的颜色选择器是UserControl,因此有以下绑定:

    SelectedColor="{Binding MyColor, Mode=TwoWay}"
    

    试图在 UserControl 的 DataContext 中找到MyColor,而不是在父窗口中。

    您可以为窗口命名并使用 ElementName 绑定:

    SelectedColor="{Binding DataContext.MyColor, ElementName=windowName, Mode=TwoWay}"
    

    或者更好的是,使用相对源绑定:

    SelectedColor="{Binding DataContext.MyColor, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay}"
    

    【讨论】:

    • 虽然我相信您的解决方案有效,但它似乎更适合“修复”UserControl(如所选答案中所述)而不是修复尝试使用用户控件的窗口。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2023-03-13
    • 2011-03-29
    • 1970-01-01
    • 2010-09-24
    • 1970-01-01
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多