使用C#绑定到WPF中的UserControl

本文关键字:中的 UserControl WPF 绑定 使用 | 更新日期: 2023-09-27 18:03:41

前言我举的控件是一个较大项目的样例。我已经在Stackoverflow上得到了社区的一些帮助,解决了控件中绑定的一些细节。令人惊讶的是,我在控件的托管形式中遇到了绑定问题。

我已经阅读并研究了DependencyProperty很多小时了。今年年初,我不是WPF的开发人员,但由于公司的死亡,我现在正在报道这个角色,我承认这是一座需要攀登的大山。

问题是我的中缺少了什么

托管表单的XAML代码

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:AControl="clr-namespace:AControl;assembly=AControl" x:Class="DependencySampler.MainWindow"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>
</Grid>

背后的代码

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new viewModelBinding();
        BeSelected =  new modelMain("Yellow", "#FFFFE0");
    }
    public modelMain BeSelected
    { 
        get { return ((viewModelBinding)DataContext).Selected; }
        set { ((viewModelBinding)DataContext).Selected = value; }
    }
}

ViewModel

    public class viewModelBinding :ViewModelBase
{
    modelMain sel = new modelMain("Red", "#FF0000");
    public modelMain Selected
    {
        get { return sel; }
        set { SetProperty(ref this.sel, value, "Selected"); }
    }
}

下一节是控件本身。

型号

    public class modelMain:ViewModelBase
{
    public modelMain(string colName, string hexval)
    {
        ColorName = colName;
        HexValue = hexval;
    }
    string colorName;
    public string ColorName
    {
        get { return colorName; }
        set { SetProperty(ref this.colorName, value, "ColorName"); }
    }
    string hexValue;
    public string HexValue
    {
        get { return hexValue; }
        set { SetProperty(ref this.hexValue, value, "HexValue"); }
    }
}

ViewModel

    public class viewModelMain:ViewModelBase
{
    ObservableCollection<modelMain> val = new ObservableCollection<modelMain>();
    public ObservableCollection<modelMain> ColorsList
    {
        get { return val; }
        set { SetProperty(ref this.val, value, "Colors"); }
    }

    modelMain selectedColor;
    public modelMain SelectedColour
    {          
        get{return selectedColor;}
        set { SetProperty(ref this.selectedColor, value, "SelectedColour"); }
    }
    public void SetCurrentColor(modelMain col)
    {
        SelectedColour = this.val.Where(x => x.ColorName == col.ColorName).FirstOrDefault(); 
    }
    public viewModelMain()
    {
        val.Add(new modelMain("Red", "#FF0000"));
        val.Add(new modelMain("Blue", "#0000FF"));
        val.Add(new modelMain("Green", "#008000"));
        val.Add(new modelMain("Yellow", "#FFFFE0"));
        SelectedColour = new modelMain("Blue", "#0000FF");
    }
}

UserControl XAML

<UserControl x:Class="AControl.UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="32" d:DesignWidth="190">
<Grid>
    <ComboBox x:Name="cboValue"
              SelectionChanged="cboValue_SelectionChanged"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Stretch"
              ItemsSource="{Binding ColorList, RelativeSource={RelativeSource AncestorType=UserControl}}"
              SelectedValue="{Binding SelectedColor, RelativeSource={RelativeSource AncestorType=UserControl}}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="10"
                               Height="10"
                               Margin="5"
                               Background="{Binding ColorName}"/>
                    <TextBlock Width="35"
                               Height="15"
                               Margin="5"
                               Text="{Binding ColorName}"/>
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox> 
</Grid>

背后的用户控制代码

    public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }
    ObservableCollection<modelMain> colorList = new viewModelMain().ColorsList;
    public ObservableCollection<modelMain> ColorList
    {
        get { return colorList; }
        set { colorList = value; }
    }

    public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(
        "SelectedColor",
        typeof(modelMain),
        typeof(UserControl1),
        new FrameworkPropertyMetadata(
            null, 
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            new PropertyChangedCallback(OnSelectedColorChanged),
            new CoerceValueCallback(CoerceSelectedColorCallback)));

    private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        UserControl1 uc = (UserControl1)d;
        uc.SelectedColor = (modelMain)e.NewValue;
    }
    private static object CoerceSelectedColorCallback(DependencyObject d, object value)
    {
        return (modelMain)value;
    }

    public modelMain SelectedColor
    {
        get { return (modelMain)GetValue(SelectedColorProperty); }
        set { SetValue(SelectedColorProperty, value); }
    }
    private void cboValue_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var dat = sender as ComboBox;
        SelectedColor = (modelMain)dat.SelectedValue;
    }
    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
        //var dat = sender as ComboBox;
        ////SelectedColor = (modelMain)dat.SelectedValue;
        //SelectedColor = (modelMain)this.SelectedColor;
    }
}

请注意,在后面的代码中有未使用的代码,但在我当时用于放置断点的样本中

我知道UserControl中不应该存在DataContext,因为它排除了宿主形式中的DataContext

问题我原以为这条线在托管形式下就足够了。

<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, Mode=OneWayToSource}"/>

但它似乎并没有达到我的预期。我可以看到BeSelected被初始化,它包含一个值,但当表单加载时,我希望黄色进入UserControl并设置DependencyPropertySelectedColor。这种情况没有发生,为什么以及如何才能实现?

使用C#绑定到WPF中的UserControl

要让您的示例工作起来,请执行以下操作(在大多数情况下,实现评论者所说的(:

托管表单的XAML代码

<AControl:UserControl1 x:Name="cboBob" HorizontalAlignment="Left" Margin="100,118,0,0" VerticalAlignment="Top" Width="200" Height="29" SelectedColor="{Binding Path=BeSelected, RelativeSource={RelativeSource AncestorType=Window}}}"/>

模式并不重要,因为MainWindow既不实现INPC,也不知道((viewModelBinding(DataContext(.Selected(因此,BeSelected(何时更改。事实上,正如乔所说,OneWayToSource不起作用。。。RelativeSource是必需的,因为BeSelected是MainWindow的属性,而不是MainWindow的DataContext。

型号主

modelMain需要实现IEquatable(就像Janne评论的那样(。为什么?因为BeSelected=new modelMain(…(创建了一个新的modelMain,它不是组合框的ItemsSource(ColorList(中的项之一。新对象可能与其中一个项具有相同的属性值,但这并不能使它们相等(不同的对象=内存中的不同地址(。IEquatable让你有机会超越它。

public class modelMain : ViewModelBase, IEquatable<modelMain>
{
  ...
  public bool Equals(modelMain other)
  {
    return (HexValue == other.HexValue);
  }
}

viewModelMain的ColorList的setter正在调用属性名称为"Colors"的SetProperty,而它应该是"ColorsList"。它没有被使用,所以它不会阻止你的例子发挥作用,但它仍然是错误的。