如何在 DependencyProperty 中传播 PropertyChanged 更改
Posted
技术标签:
【中文标题】如何在 DependencyProperty 中传播 PropertyChanged 更改【英文标题】:How to propagate PropertyChanged changes in DependencyProperty 【发布时间】:2011-09-26 09:07:57 【问题描述】:我有一个实现 INotifyPropertyChanged 的类。此类的实例在 Window 中声明为 DependencyProperty,例如,
public IMyClass MyClass
get return (IMyClass)GetValue(MyClassProperty);
set SetValue(MyClassProperty, value);
public static readonly DependencyProperty MyClassProperty=
DependencyProperty.Register("MyClass", typeof(IMyClass), typeof(MainWindow), new UIPropertyMetadata(null));
在 XAML 中,我有一个使用绑定到此类的元素
Text="Binding MyClass, Converter=StaticResource someConverter
每当我更改 MyClass 中的属性时,我都希望触发 someConverter。但是,只有当我完全换掉 MyClass 时才会发生这种情况。有没有办法将 DependencyProperty 更新绑定到我的 MyClass PropertyChanged?
更新。本着 AresAvatar 解决方案的精神,这是我们目前所拥有的。剩下的问题是如何调用 InvalidateProperty(不让 MyClass 跟踪它......)
public IMyClass MyClass
get return (IMyClass)GetValue(MyClassProperty);
set SetValue(MyClassProperty, value);
public static readonly DependencyProperty MyClassProperty =
DependencyProperty.Register("MyClass", typeof(IMyClass), typeof(MainWindow),
new UIPropertyMetadata(null, new PropertyChangedCallback(OnMyClassChanged)));
private static void OnMyClassChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (e.OldValue != null)
((IMyClass)e.OldValue).PropertyChanged -= ((MainWindow)d).MyClass_PropertyChanged;
if (e.NewValue != null)
((IMyClass)e.NewValue).PropertyChanged += ((MainWindow)d).MyClass_PropertyChanged;
private void MyClass_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
this.InvalidateProperty(MyClassProperty); <----- still does not refresh binding, but called.
【问题讨论】:
我遇到了类似的问题,仍然没有解决:***.com/questions/6363883/… dthorpe,我在下面的回答也应该适用于您的问题。您想将 NotifyPropertyChanged 挂钩到绑定失效中,就像这里的 tofutim 一样。 @dthorpe - 你的问题很有帮助。 尝试使用这个:BindingExpressionBase exp = BindingOperations.GetBindingExpressionBase(this, Container.MyClassProperty); if (exp != null) exp.UpdateTarget(); 我尝试了 UpdateTarget 和 InvalidateProperty 选项,但没有成功。然而,他们正在被召唤。我认为底层 Binding 代码必须对对象是否相同进行一些检查,并且不能强制重新调用 Converter。 【参考方案1】:转换器不应该比简单的转换做更多的工作,你的问题听起来像转换器使用对象的很多属性来创建一些组合值。使用MultiBinding
代替它挂钩到您需要的对象上的所有不同属性,这样MultiBinding
上的MultiValueConverter
将在任何这些属性发生变化时触发。
此外,由于您似乎在创建文本,因此您可能无需使用任何转换器就可以逃脱,因为 StringFormat
可能就足够了。
【讨论】:
实际上,我已经在使用 MultiBinding,并将 MyClass 作为绑定对象之一。您是否建议我 MultiBind 到 MyClass.Prop1、MyClass.Prop2 和 MyClass.Prop3?肯定有办法说这个对象已经改变了? @toutim:这就是我的建议,是的。我不知道有什么方法可以通知此类更改,如果有办法,我也不建议这样做。编辑:AresAvatar 说的可能就是这样。 总而言之,我永远无法让 InvalidateProperty 重新触发转换器,您的解决方案虽然我从美学的角度抗拒,但实际上有效!【参考方案2】:在 MyClass 中,实现 NotifyPropertyChanged 事件。然后将属性更改回调添加到您的 MyClass DependencyProperty。在 DP 的属性更改回调中,将新的 MyClass NotifyPropertyChanged 事件挂钩到第二个回调函数(如果有的话,使用 -= 运算符取消挂钩之前的值)。在第二个回调函数中,调用DependencyObject.InvalidateProperty 以便更新绑定。
编辑:您可能需要触发绑定更新:
BindingExpressionBase exp = BindingOperations.GetBindingExpressionBase(this, Container.MyClassProperty);
if (exp != null)
exp.UpdateTarget();
class MyClass : INotifyPropertyChanged
/// <summary>
/// Event raised when a property is changed
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the property changed event
/// </summary>
/// <param name="e">The arguments to pass</param>
protected void OnPropertyChanged(PropertyChangedEventArgs e)
if (PropertyChanged != null)
PropertyChanged(this, e);
/// <summary>
/// Notify for property changed
/// </summary>
/// <param name="name">Property name</param>
protected void NotifyPropertyChanged(string name)
OnPropertyChanged(new PropertyChangedEventArgs(name));
/// <summary>
/// The parent container object
/// </summary>
public Container Parent get; set;
// Some data
int x;
class Container : DependencyObject
public static readonly DependencyProperty MyClassProperty = DependencyProperty.Register("MyClass", typeof(MyClass), typeof(Container), new FrameworkPropertyMetadata(MyClassPropChanged));
public MyClass MyClass
get return (MyClass)GetValue(MyClassProperty);
set SetValue(MyClassProperty, value);
void MyClassPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
Container ct = d as Container;
if (ct == null)
return;
MyClass oldc = e.OldValue as MyClass;
if (oldc != null)
oldc.PropertyChanged -= new PropertyChangedEventHandler(MyClass_PropertyChanged);
oldc.Parent = null;
MyClass newc = e.NewValue as MyClass;
if (newc != null)
newc.Parent = ct;
newc.PropertyChanged += new PropertyChangedEventHandler(MyClass_PropertyChanged);
void MyClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
MyClass mc = sender as MyClass;
if (mc == null || mc.Parent == null)
return;
mc.Parent.InvalidateProperty(Container.MyClassProperty);
【讨论】:
为什么会有多个事件挂钩? 如果我对他的理解正确,他希望更改设置到 DP 中的 MyClass 实例,以导致 DP 无效。为此,您需要使用 MyClass 实例的事件,以及 DP 中的属性更改事件。 @AresAvatar 我设置了 PropertyChangedCallback(显然必须是静态的)以解开 e.OldValue PropertyChanged 并挂钩 e.NewValue PropertyChanged - 但是如何将 DependencyObject 传递给 PropertyChanged 回调?跨度> @tofutim:将 NotifyPropertyChanged 更改回调设置为包含您的 DependencyProperty 的类的成员函数。在 DP 属性更改回调中,您将获得 MyClass 的副本。 PropertyChangedCallback 坚持使用静态回调,它会一直传播。在 DP 回调中,您确实获得了 MyClass 的副本,但我还没有找到将其传递给 PropertyChanged 的方法(它确实有发送者 - 但这是 MyClass)。目前我唯一能想到的就是让 MyClass 跟踪 Dp,但这似乎很奇怪。【参考方案3】:我发现的唯一技术是在策略性放置的事件处理程序(例如 LostFocus)中调用绑定的 UpdateSource 方法。
private void mycontrol_LostFocus(object sender, RoutedEventArgs e)
if (mycontrol.IsModified)
var binding = mycontrol.GetBindingExpression(MyControl.FooBarProperty);
binding.UpdateSource();
如果您不关心聊天或者您的控件没有获取输入焦点,您可以在 mycontrol_PropertyChanged 事件或类似事件中执行此操作。但是,在每次属性更改或每次击键时强制执行转换周期可能会干扰验证。
【讨论】:
以上是关于如何在 DependencyProperty 中传播 PropertyChanged 更改的主要内容,如果未能解决你的问题,请参考以下文章
如何使 Binding 尊重 DependencyProperty 值强制?
ViewModel 中的 INotifyPropertyChanged 与 DependencyProperty
如何使 WPF UserControl 中的一个 DependencyProperty 更改另一个 [重复]
从 xaml 中清除 dependencyProperty 值