如何在DependencyProperty中传播PropertyChanged更改

时间:2011-06-30 22:41:21

标签: c# wpf xaml data-binding dependency-properties

我有一个实现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.
    }

3 个答案:

答案 0 :(得分:7)

转换器不应该做比简单转换更多的工作,你的问题听起来像转换器使用对象的许多属性来创建一些组合值。使用MultiBinding代替您所需对象的所有不同属性,这样MultiBinding上的MultiValueConverter将会触发,如果其中任何属性发生变化。

此外,由于您似乎创建了文本,因此您可以在不使用任何转换器的情况下离开,因为StringFormat可能就足够了。

答案 1 :(得分:1)

在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);
    }
}

答案 2 :(得分:1)

我发现的唯一技术是在策略性放置的事件处理程序中调用绑定的UpdateSource方法,例如LostFocus。

private void mycontrol_LostFocus(object sender, RoutedEventArgs e)
{
    if (mycontrol.IsModified)
    {
        var binding = mycontrol.GetBindingExpression(MyControl.FooBarProperty);
        binding.UpdateSource();
    }
}

如果您不关心chattiness或者您的控件不关注输入焦点,您可以在mycontrol_PropertyChanged事件或类似事件中执行此操作。但是,在每次更改属性或每次击键时强制转换周期都可能会影响验证。