65.9K
CodeProject 正在变化。 阅读更多。
Home

绑定到非 DependencyProperty 的属性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (8投票s)

2010 年 4 月 7 日

Ms-PL

2分钟阅读

viewsIcon

57169

许多控件公开的属性不是 DependencyProperty,因此您无法对其进行绑定。在某些其他情况下,您只有一个 getter 作为访问器,你也无法对其进行绑定...

许多控件公开的属性不是 DependencyProperty,因此您无法对其进行绑定。在某些其他情况下,您只有一个 getter 作为访问器,你也无法对其进行绑定...

例如,Office Ribbon 的 Ribbon Group 或转换器的参数就是这种情况。

如果你曾经尝试过这样做,你肯定会抛出一个异常

无法在类型为 'Tralala' 的 'SetCEDEJDED' 属性上设置 'Binding'。
'Binding' 只能设置在 DependencyObjectDependencyProperty 上。

在这篇文章中,我们将探索一种解决方法…

主要思想是使用一种代理/观察者(可以在这篇文章中找到一个定义),它会将源对象上的每个更改反映到目标对象,反之亦然。

以下是解决方案的主要部分。

规范:我们将使用的 XAML 代码

以下代码片段描述了我们将在 XAML 中如何使用我们的代理。没有代码隐藏。

<Window x:Class="BindOnNonDependencyProperty.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:us="clr-namespace:BindOnNonDependencyProperty"
    Title="BindOnNonDependencyProperty" >
  <DockPanel >
    <TextBox x:Name="myTextBox" DockPanel.Dock="Top"  />
    <TextBox x:Name="monTextBlockCible"  DockPanel.Dock="Top"  />
    <us:ExtendedBinding Source="{Binding ElementName=myTextBox,Path=Text,Mode=TwoWay}"
              Target="{Binding ElementName=monTextBlockCible,Path=Text,Mode=TwoWay}"
              />
  </DockPanel>
</Window>

我们代理/观察者的正确基类

我们将其称为 ExtendedBinding,并且它必须至少从 DependencyObject 继承,才能拥有 DependencyProperty。但是,将 DependencyObject 添加到我们的 XAML 的唯一方法是将其添加到 resourceDictonary 中。

这是一个缺点,因为这样做后,它将不再在控件的树中,因此无法对其属性之一进行绑定。请注意,仍然可以将其用作 XAML 中其他位置的目标,但您无法对其属性之一进行绑定。此代码将不起作用

<Windows.Resources>
   <MyDependencyObject x:Key="myKey" 
	MyProperty="{Binding Tralala, ElementName=myTarget}" />
</Windows.Resources>

然后,要将其放入控件的树中,您只需使其成为一个 UIElement 即可... 不,因为在框架的实际版本中,您将没有 DataContext 的继承,并且禁止使用 'ElementName' 绑定。 幸运的是,有一个解决方案,我们的代理必须从 FrameworkElement 继承,一切都会顺利进行!

DependencyProperties

我们将添加两个 dependencyProperties,一个将是目标,第二个将是源。
这些 DP 将通过使用 FrameworkPropertyMetadata 来定制,以启用这些功能

  • 将使用 TwoWay 模式完成绑定
  • 使用的 UpdateSourceTrigger 将是 PropertyChanged 事件

工作原理

我们代理的核心是覆盖 DependencyObject 的 OnPropertyChanged 方法。 源或目标上的每次更改都会更新其对应方。

我们必须注意不要陷入循环:当我们更新目标或源时,我们也会引发一个 PropertyChanged 事件,我们必须忽略这个事件...

最终代码

public class ExtendedBinding : FrameworkElement
  {
    #region Source DP
    //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Source
    {
      get { return GetValue(ExtendedBinding.SourceProperty); }
      set { SetValue(ExtendedBinding.SourceProperty, value); }
    }
    #endregion
 
    #region Target DP
      //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty TargetProperty =
      DependencyProperty.Register("Target", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Target
    {
      get { return GetValue(ExtendedBinding.TargetProperty); }
      set { SetValue(ExtendedBinding.TargetProperty, value); }
    }
    #endregion
 
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
      base.OnPropertyChanged(e);
      if (e.Property.Name == ExtendedBinding.SourceProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Target = Source;
      }
      else if (e.Property.Name == ExtendedBinding.TargetProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Source = Target;
      }
    }
  }
Shout it kick it on DotNetKicks.com
© . All rights reserved.