Как распространять изменения PropertyChanged в DependencyProperty

У меня есть класс, который реализует INotifyPropertyChanged. Экземпляр этого класса объявляется как 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.
    }

Ответ 1

Конвертеры не должны делать больше работы, чем простые преобразования, ваш вопрос звучит так, как будто конвертер использует множество свойств объекта для создания некоторого комбинированного значения. Используйте MultiBinding вместо этого, который перехватывает все различные свойства объекта, который вам нужен, таким образом MultiValueConverter, при этом MultiBinding будет срабатывать, если какое-либо из этих свойств изменится.

Кроме того, поскольку вы, кажется, создаете текст, вы можете уйти без использования какого-либо конвертера вообще, поскольку StringFormat может быть достаточно.

Ответ 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);
    }
}

Ответ 3

Единственный метод, который я нашел, - это вызов метода привязки UpdateSource в стратегически размещенном обработчике событий, таком как LostFocus.

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

Если вы не заботитесь о chattiness, или если ваш элемент управления не принимает фокус ввода, вы можете сделать это в событии mycontrol_PropertyChanged или аналогичном. Тем не менее, вызывая цикл преобразования при каждом изменении свойств, или каждое нажатие клавиши может мешать проверке.