Как распространять изменения PropertyChanged в DependencyProperty - PullRequest
11 голосов
/ 01 июля 2011

У меня есть класс, который реализует INotifyPropertyChanged. Экземпляр этого класса объявлен как DependencyProperty в Window, например,

    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 ]

7 голосов
/ 01 июля 2011

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

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

1 голос
/ 08 июля 2011

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

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

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

1 голос
/ 01 июля 2011

В 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);
    }
}
...