[英]Implementing INotifyPropertyChanged for nested properties
我有一個 Person 類:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name{
get { return _name; }
set {
if ( _name != value ) {
_name = value;
OnPropertyChanged( "Name" );
}
}
private Address _primaryAddress;
public Address PrimaryAddress {
get { return _primaryAddress; }
set {
if ( _primaryAddress != value ) {
_primaryAddress = value;
OnPropertyChanged( "PrimaryAddress" );
}
}
//OnPropertyChanged code goes here
}
我有一個地址類:
public class Address : INotifyPropertyChanged
{
private string _streetone;
public string StreetOne{
get { return _streetone; }
set {
if ( _streetone != value ) {
_streetone = value;
OnPropertyChanged( "StreetOne" );
}
}
//Other fields here
//OnPropertyChanged code goes here
}
我有一個視圖模型:
public class MyViewModel
{
//constructor and other stuff here
private Person _person;
public Person Person{
get { return _person; }
set {
if ( _person != value ) {
_person = value;
OnPropertyChanged( "Person" );
}
}
}
我有一個視圖,其中包含以下幾行:
<TextBox Text="{Binding Person.Name, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged />
<TextBox Text="{Binding Person.Address.StreetOne, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged />
當視圖加載時,這兩個值都顯示在文本框中。
對第一個文本框的更改將在 MyViewModel 中觸發 OnPropertyChanged OnPropertyChanged( "Person" )
。 偉大的。
對第二個文本框("Person.Address.StreetOne")
的更改不會在 MyViewModel 中觸發 OnPropertyChanged OnPropertyChanged( "Person" )
。 這意味着它不調用 Person 對象的 SET 方法。 不是很好。 有趣的是,Address 類中 StreetOne 的 SET 方法被調用了。
當Person.Address.StreetOne
更改時,如何獲取 ViewModel 中 Person 對象的 SET 方法?
我是否需要展平我的數據,以便 SteetOne 在 Person 而不是 Address?
謝謝!
雖然向 ViewModel 添加“傳遞”屬性是一個很好的解決方案,但它很快就會變得站不住腳。 標准的替代方法是傳播更改,如下所示:
public Address PrimaryAddress {
get => _primaryAddress;
set {
if ( _primaryAddress != value )
{
//Clean-up old event handler:
if(_primaryAddress != null)
_primaryAddress.PropertyChanged -= AddressChanged;
_primaryAddress = value;
if (_primaryAddress != null)
_primaryAddress.PropertyChanged += AddressChanged;
OnPropertyChanged( "PrimaryAddress" );
}
void AddressChanged(object sender, PropertyChangedEventArgs args)
=> OnPropertyChanged("PrimaryAddress");
}
}
現在更改通知從地址傳播到人員。
編輯:將處理程序移動到 c# 7 本地函數。
如果你想調用 viewmodel SET 你可以創建一個街道屬性
public class MyViewModel
{
//constructor and other stuff here
public string Street{
get { return this.Person.PrimaryAddress.StreetOne; }
set {
if ( this.Person.PrimaryAddress.StreetOne!= value ) {
this.Person.PrimaryAddress.StreetOne = value;
OnPropertyChanged( "Street" );
}
}
}
xml
<TextBox Text="{Binding Street, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged />
但這種解決方案有其缺點。 我在我的項目中選擇 Reeds 答案
當 Person.Address.StreetOne 更改時,如何獲取 ViewModel 中 Person 對象的 SET 方法?
你為什么要這樣做? 它不應該是必需的 - 您只需要StreetOne
屬性更改事件。
我是否需要展平我的數據,以便 SteetOne 在 Person 而不是 Address?
如果您想真正觸發它,則不需要將其展平(盡管這是一個選項)。 您可以在 Person 類中訂閱Address
的PropertyChanged
事件,並在Person
更改時引發“Address”事件。 然而,這不應該是必要的。
由於我無法找到現成的解決方案,因此我根據 Pieters(和 Marks)的建議(謝謝!)進行了自定義實現。
使用這些類,您將收到有關深層對象樹中任何更改的通知,這適用於任何INotifyPropertyChanged
實現類型和INotifyCollectionChanged
* 實現集合(顯然,我為此使用了ObservableCollection
)。
我希望這是一個非常干凈和優雅的解決方案,雖然它沒有經過全面測試並且還有改進的空間。 它非常易於使用,只需使用它的靜態Create
方法創建一個ChangeListener
實例並傳遞您的INotifyPropertyChanged
:
var listener = ChangeListener.Create(myViewModel);
listener.PropertyChanged +=
new PropertyChangedEventHandler(listener_PropertyChanged);
PropertyChangedEventArgs
提供一個PropertyName
,它將始終是您的對象的完整“路徑”。 例如,如果您更改 Persons 的“BestFriend”名稱, PropertyName
將為“BestFriend.Name”,如果BestFriend
有一個 Children 集合並且您更改它的 Age,則該值將為“BestFriend.Children[].Age”等等。 當您的對象被銷毀時,不要忘記Dispose
,然后它將(希望)完全取消訂閱所有事件偵聽器。
它在 .NET(在 4 中測試)和 Silverlight(在 4 中測試)編譯。 因為代碼分為三個類,我已將代碼發布到gist 705450 ,您可以在其中獲取所有內容: https ://gist.github.com/705450 **
*) 代碼工作的一個原因是ObservableCollection
還實現了INotifyPropertyChanged
,否則它不會按預期工作,這是一個已知的警告
**) 免費使用,在MIT 許可下發布
您的屬性更改通知中有拼寫錯誤:
OnPropertyChanged( "SteetOne" );
應該
OnPropertyChanged( "StreetOne" );
請查看EverCodo.ChangesMonitoring 。 這是一個框架,用於處理嵌套對象和集合的任意層次結構上的 PropertyChanged 和 CollectionChanged 事件。
創建一個監視器來處理對象樹的所有更改事件:
_ChangesMonitor = ChangesMonitor.Create(Root);
_ChangesMonitor.Changed += ChangesMonitor_Changed;
對對象樹進行任意修改(所有這些都將被處理):
Root.Children[5].Children[3].Children[1].Metadata.Tags.Add("Some tag");
Root.Children[5].Children[3].Metadata = new Metadata();
Root.Children[5].Children[3].Metadata.Description = "Some description";
Root.Children[5].Name = "Some name";
Root.Children[5].Children = new ObservableCollection<Entity>();
在一個地方處理所有事件:
private void ChangesMonitor_Changed(object sender, MonitoredObjectChangedEventArgs args)
{
// inspect args parameter for detailed information about the event
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.