INotifyPropertyChanged в подклассе

Я хочу привязать TextBox в окне к свойству, содержащемуся в классе, который является переменной модели представления, и убедиться, что событие INotifyPropertyChanged PropertyChanged распространяется от класса к родителю.

Поясню на примере:

(Windows DataContext установлен на экземпляр ViewModel)

public class ViewModel {
    private OtherClass classInstance = new OtherClass();

    public int Attribute {
        get { return classInstance.Attribute; }
    }
}

public class OtherClass : INotifyPropertyChanged {
    private int _attribute;
    public int Attribute {
        get { return _attribute; }
        set { 
            _attribute = value;
            PropertyChanged("Attribute");
        }
    }
    ...
}

The problem in this example is that, when Attribute changes, the bound Textbox does not update the binding since I assume it's listening to the ViewModel's PropertyChanged event and not that of the instance of OtherClass.

Есть идеи, как исправить эту ситуацию? Я думал о том, чтобы связать INotifyPropertyChanged OtherClass с INotifyPropertyChanged его родителя, но должен быть лучший способ.


person aleph_null    schedule 27.09.2011    source источник


Ответы (4)


Почему бы не привязать напрямую к свойству класса вместо использования прокси?

public class ViewModel {
    private OtherClass classInstance = new OtherClass();

    public OtherClass MyOtherClass {
        get { return classInstance; }
    }
}

Затем в вашей привязке вы можете просто ссылаться на свойство через подкласс

{Binding MyOtherClass.Attribute}

Потрясающе простой пример, но он работает!

Код позади:

public partial class MainWindow : Window {
   private readonly SomeClass _someClass = new SomeClass();

   public MainWindow() {
      InitializeComponent();
      DataContext = _someClass;
   }
}

public class SomeClass: INotifyPropertyChanged {      
   private readonly SomeSubClass _mySubClass = new SomeSubClass();

   public SomeSubClass MySubClass {
      get { return _mySubClass; }
   }

   private String _name;
   public String Name {
      get { return _name; }
      set {
         _name = value;
         OnPropertyChanged("Name");
      }
   }

   //Property Change Stuff
}

public class SomeSubClass : INotifyPropertyChanged {
   private String _name;
   public String Name {
      get {
         return _name;
      }
      set {
         _name = value;
         OnPropertyChanged("Name");
      }
   }

   //Property Change Stuff
}

XAML:

<Window x:Class="JWC.Examples.WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow">
    <StackPanel>
        <Label Content="Name" VerticalAlignment="Top" />
        <TextBox Text="{Binding Name}" />
        <Label Content="SubClass.Name" />
        <TextBox Text="{Binding MySubClass.Name}" />
        <Label Content="Bound to Name" />
        <TextBlock Text="{Binding Name}" />
        <Label Content="Bound to MySubClass.Name" />
        <TextBlock Text="{Binding MySubClass.Name}" />
    </StackPanel>
</Window>
person Josh    schedule 27.09.2011
comment
Офигительные простые примеры - лучший пример :) - person Moulde; 08.11.2011
comment
Это нецелесообразно, если SomeSubClass содержит кучу свойств. Вы действительно собираетесь дублировать все эти свойства? - person Sean; 13.03.2017
comment
@Sean - я не уверен, что вы подразумеваете под дублированием всех этих свойств. В примере просто совпадение, что Name определено для родителя и дочернего элемента. Это не имеет значения для основного вопроса. В основном вы можете напрямую привязываться к свойствам дочернего класса вместо того, чтобы пытаться использовать прокси-переменные. В этом смысле дублирования не будет. - person Josh; 14.03.2017
comment
Я имею в виду, что я должен полностью понять примеры, прежде чем комментировать :) Я думал, что вы углубляетесь в имя, создавая прокси в содержащем классе. Наличие обоих свойств имени Имя сбило меня с толку, извиняюсь. - person Sean; 15.03.2017

Вам нужно будет сделать что-то вроде этого:

public class ViewModel {
   public ViewModel() {
      classInstance = new OtherClass();
      classInstance.PropertyChanged += HandleAttributeChanged; 
   }

   private void HandleAttributeChanged(object Sender, NotifyPropertyChangedEventArgs args) {
      PropertyChanged("Attribute");
   }
}

Я не показываю это здесь, но вы также должны реализовать IDisposable и отписаться от события PropertyChanged на своем дочернем элементе, иначе произойдет утечка памяти.

В качестве альтернативы вы можете открыть classInstance как общедоступное свойство и установить привязку к: ViewModel.classInstance. Обратите внимание, что это должно быть свойство, а не само поле.

person CodingGorilla    schedule 27.09.2011
comment
У меня работает изменение NotifyPropertyChangedEventArgs на PropertyChangedEventArgs. - person Carlos; 05.03.2017

Я думаю, что родительский класс должен подписаться на дочернее событие propertyCahnged.. что-то вроде:

public class ViewModel
{
    private OtherClass classInstance = new OtherClass();

    public ViewModel()
    {
        classInstance.PropertyChanged += NotifyChildAttributeChanged;
    }
    public int Attribute
    {
        get { return classInstance.Attribute; }
    }
} 

NotifyChildAttributeChanged — это, по сути, метод, который прослушивает только свойство «Атрибут» и запускает собственное уведомление PropertyChanged.

Конечно, наш родительский класс должен реализовать INotifyPropertyChanged, а также все ViewModels (предпочтительно), и ваш DataContext обнаружит изменение.

person Anas Karkoukli    schedule 27.09.2011

Чтобы обойти это, вам также необходимо реализовать INotifyPropertyChanged в вашей модели представления. Просто добавьте интерфейс и событие, а все остальное позаботится само о себе — не нужно связывать события/вызовы вместе.

Проверьте это, чтобы использовать отражение, чтобы получить свойство.

http://tsells.wordpress.com/2011/02/08/using-reflection-with-wpf-and-the-inotifypropertychanged-interface/

person tsells    schedule 29.09.2011