误解数据绑定基础和DataContexts – 长篇大论

我一直在几个简单的情况下使用数据绑定,取得了相当不错的成功。 通常我只使用INotifyPropertyChanged来启用我的代码隐藏来修改屏幕上的GUI值,而不是为所有内容实现依赖项属性。

我正在使用LED控件来了解有关用户控件中数据绑定的更多信息,并被迫使用依赖属性,因为VS2008告诉我必须这样做。 我的应用程序很简单 – 我有一个窗口,显示几个LED控件,每个控件上面都有一个数字,可选择一个控件。 LED应该可以使用默认颜色进行定义,以及更改状态。

我开始写一个LED控制,看起来非常好。 首先,我开始使用这样的代码:

LED.xaml

                          

这样可以很好地吸收LED。 然后我将LEDSize,LEDLabel和LEDColor绑定到Ellipse属性,方法是设置this.DataContext = this就像我一直这样:

LED.xaml.cs

 ///  /// Interaction logic for LED.xaml ///  public partial class LED : UserControl, INotifyPropertyChanged { private Brush state_color_; public Brush LEDColor { get { return state_color_; } set { state_color_ = value; OnPropertyChanged( "LEDColor"); } } private int led_size_; public int LEDSize { get { return led_size_; } set { led_size_ = value; OnPropertyChanged( "LEDSize"); } } private string led_label_; public string LEDLabel { get { return led_label_; } set { led_label_ = value; OnPropertyChanged( "LEDLabel"); } } public LED() { InitializeComponent(); this.DataContext = this; } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged( string property_name) { if( PropertyChanged != null) PropertyChanged( this, new PropertyChangedEventArgs( property_name)); } #endregion } 

此时,我可以更改属性值,并看到LED改变大小,颜色和标签。 大!

我希望LED控件可以在我编写的其他小部件中重复使用,下一步是创建另一个UserControl(在单独的程序IOView ),称为IOViewIOView在这一点上非常基础:

IOView.xaml

           

请注意,我可以在设计时修改XAML中的LED属性,一切都按预期工作:

替代文字

然后我盲目地尝试将LEDColor数据绑定到我的IOView,VS2008告诉我“A’Binding”不能设置在’LED’类型的’LEDColor’属性上。’Binding’只能在DependencyObject的DependencyProperty上设置“。 哎呀! 我甚至没有意识到,因为我之前没有制作过自己的GUI控件。 由于LEDColor已经数据绑定到Ellipse,我添加了一个名为Color的DependencyProperty。

LED.xaml.cs

  public static DependencyProperty ColorProperty = DependencyProperty.Register( "Color", typeof(Brush), typeof(LED)); public Brush Color { get { return (Brush)GetValue(ColorProperty); } set { SetValue( ColorProperty, value); LEDColor = value; } } 

请注意,我在setter中设置属性LEDColor ,因为Ellipse知道它应该是什么颜色。

接下来的步骤涉及通过绑定到IOView.InputColor来设置IOView中LED的颜色:

IOView.xaml.cs:

 ///  /// Interaction logic for IOView.xaml ///  public partial class IOView : UserControl, INotifyPropertyChanged { private Int32 index_; public Int32 Index { get { return index_; } set { index_ = value; OnPropertyChanged( "Index"); } } private Brush color_; public Brush InputColor { get { return color_; } set { color_ = value; OnPropertyChanged( "InputColor"); } } private Boolean state_; public Boolean State { get { return state_; } set { state_ = value; OnPropertyChanged( "State"); } } public IOView() { InitializeComponent(); this.DataContext = this; } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged( string property_name) { if( PropertyChanged != null) PropertyChanged( this, new PropertyChangedEventArgs( property_name)); } #endregion } 

在IOView.xaml中,我将LED更改为:

  

但它不起作用,因为输出窗口中出现以下错误:

BindingExpression路径错误:’object”’LED’(Name =”)’上找不到’InputColor’属性。 BindingExpression:路径= InputColor; DataItem =’LED’(Name =”); 目标元素是’LED’(Name =”); target属性是’Color’(类型’Brush’)

嗯…所以出于某种原因,我的DataBinding搞砸了。 我可以通过数据绑定使LED自行工作,但是一旦我将其包装在另一个控件中并设置其datacontext,它就不起作用。 我不确定在这一点上要尝试什么。

我希望得到尽可能详细的答案。 我知道我可以重新模仿一个CheckBox来获得相同的结果,但这对我来说是一个实验,我试图理解如何数据绑定到控件的后代。

关于这一切有很多话要说,但让我看看我是否可以提供解决你的一些误解的指针:

  • 为了使属性成为绑定的目标 ,该属性必须是依赖属性。 WPF(和Silverlight)使用依赖项属性作为跟踪更改,支持值优先级(对于动画等)以及一堆其他有用的东西的方法。 请注意,我说“目标”。 绑定可以是支持更改通知的任何旧对象。
  • UserControl本身中设置UserControlDataContext被认为是不好的做法,因为您的控件的任何使用者都可以更改它,这样做会破坏控件中依赖于该上下文的任何绑定
  • 除了上述观点之外,另一个问题是您将在使用依赖于用户控件“上方”的数据上下文的代码时中断任何绑定。 这解释了您在InputColor未成功绑定时看到的问题。 InputColor属性位于主机控件( IOView )提供的数据上下文中,但LED的数据上下文设置为LED本身,因此如果不进一步限定绑定,则无法找到该属性。

遵循此建议会导致以下实施(未经测试):

LED.xaml.cs

 public partial class LED : UserControl { public static readonly DependencyProperty LEDColorProperty = DependencyProperty.Register( "LEDColor", typeof(Brush), typeof(LED)); public Brush LEDColor { get { return this.GetValue(LEDColorProperty) as Brush; } set { this.SetValue(LEDColorProperty, value); } } // LEDSize and LEDLabel omitted for brevity, but they're very similar to LEDColor public LED() { InitializeComponent(); } } 

LED.xaml

                         

IOView.xaml