C# 如何传播集合中对象的属性更改通知
假设我有这样的课C# 如何传播集合中对象的属性更改通知,c#,event-handling,inotifypropertychanged,windows-forms-designer,C#,Event Handling,Inotifypropertychanged,Windows Forms Designer,假设我有这样的课 public class R { protected string name; protected List<S> listOfObjectS; } public class S { private string name, ID; private A objectA; } public class A { private string name; private int count; } 公共类R { 受保护的
public class R
{
protected string name;
protected List<S> listOfObjectS;
}
public class S
{
private string name, ID;
private A objectA;
}
public class A
{
private string name;
private int count;
}
公共类R
{
受保护的字符串名称;
受保护的对象列表;
}
公共类
{
私有字符串名称,ID;
私人反对;
}
公共A类
{
私有字符串名称;
私人整数计数;
}
如果用户有两个打开的视图,一个显示R
的实例,另一个允许用户修改a
的实例,我需要在用户更改a
的任何实例时更改R
的视图
如果用户更改了
a
实例的属性,传播该更改(通过S
实例)的最佳方式是什么因此,R
的所有实例都会显示A
?编辑的新状态:彻底检查此答案,使其更具体地针对问题,因为标记显示您已经知道
您需要在类A
和类S
中实现INotifyPropertyChanged
。使objectA
只能通过一个属性设置,该属性在a
中更改属性时,将在S上引发PropertyChanged
事件。例如:
public class A : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int count;
public int Count
{
get { return count; }
set { count = value; OnPropertyChanged("Count"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class R
{
protected string name;
protected System.Collections.ObjectModel.ObservableCollection<S> ListOfObjectS { get; private set; }
public R()
{
// Use ObservableCollection instead.
ListOfObjectS = new ObservableCollection<S>();
// Subscribe to all changes to the collection.
ListOfObjectS.CollectionChanged += listOfObjectS_CollectionChanged;
}
void listOfObjectS_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
// When items are removed, unsubscribe from property change notifications.
var oldItems = (e.OldItems ?? new INotifyPropertyChanged[0]).OfType<INotifyPropertyChanged>();
foreach (var item in oldItems)
item.PropertyChanged -= item_PropertyChanged;
}
// When item(s) are added, subscribe to property notifications.
if (e.Action == NotifyCollectionChangedAction.Add)
{
var newItems = (e.NewItems ?? new INotifyPropertyChanged[0]).OfType<INotifyPropertyChanged>();
foreach (var item in newItems)
item.PropertyChanged += item_PropertyChanged;
}
// NOTE: I'm not handling NotifyCollectionChangedAction.Reset.
// You'll want to look into when this event is raised and handle it
// in a special fashion.
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.StartsWith("ObjectA."))
{
// Refresh any dependent views, forms, controls, whatever...
}
}
}
。。。和类S
public class S : INotifyPropertyChanged
{
private string name, ID;
private A objectA;
public A ObjectA
{
get { return objectA; }
set
{
var old = objectA;
objectA = value;
// Remove the event subscription from the old instance.
if (old != null) old.PropertyChanged -= objectA_PropertyChanged;
// Add the event subscription to the new instance.
if (objectA != null) objectA.PropertyChanged += objectA_PropertyChanged;
OnPropertyChanged("ObjectA");
}
}
void objectA_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Propagate the change to any listeners. Prefix with ObjectA so listeners can tell the difference.
OnPropertyChanged("ObjectA." + e.PropertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
对于类R
,使用ObservableCollection
而不是List
,订阅其CollectionChanged
事件,并监视对象何时添加或删除到对象列表中。添加后,订阅S
的PropertyChanged
事件。然后更新了R
的视图。例如:
public class A : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int count;
public int Count
{
get { return count; }
set { count = value; OnPropertyChanged("Count"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class R
{
protected string name;
protected System.Collections.ObjectModel.ObservableCollection<S> ListOfObjectS { get; private set; }
public R()
{
// Use ObservableCollection instead.
ListOfObjectS = new ObservableCollection<S>();
// Subscribe to all changes to the collection.
ListOfObjectS.CollectionChanged += listOfObjectS_CollectionChanged;
}
void listOfObjectS_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
// When items are removed, unsubscribe from property change notifications.
var oldItems = (e.OldItems ?? new INotifyPropertyChanged[0]).OfType<INotifyPropertyChanged>();
foreach (var item in oldItems)
item.PropertyChanged -= item_PropertyChanged;
}
// When item(s) are added, subscribe to property notifications.
if (e.Action == NotifyCollectionChangedAction.Add)
{
var newItems = (e.NewItems ?? new INotifyPropertyChanged[0]).OfType<INotifyPropertyChanged>();
foreach (var item in newItems)
item.PropertyChanged += item_PropertyChanged;
}
// NOTE: I'm not handling NotifyCollectionChangedAction.Reset.
// You'll want to look into when this event is raised and handle it
// in a special fashion.
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.StartsWith("ObjectA."))
{
// Refresh any dependent views, forms, controls, whatever...
}
}
}
公共类R
{
受保护的字符串名称;
受保护的System.Collections.ObjectModel.ObservableCollection列表对象{get;private set;}
公共资源(
{
//改用ObservableCollection。
ListOfObjectS=新的ObservableCollection();
//订阅对集合的所有更改。
ListOfObjectS.CollectionChanged+=ListOfObjectS\u CollectionChanged;
}
无效对象列表\u CollectionChanged(对象发送方,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if(e.Action==NotifyCollectionChangedAction.Remove)
{
//删除项目后,取消订阅属性更改通知。
var oldItems=(e.oldItems??new INotifyPropertyChanged[0])of type();
foreach(旧项目中的var项目)
item.PropertyChanged-=item_PropertyChanged;
}
//添加项目后,订阅属性通知。
if(e.Action==NotifyCollectionChangedAction.Add)
{
var newItems=(e.newItems??new INotifyPropertyChanged[0])of type();
foreach(newItems中的var项)
item.PropertyChanged+=item_PropertyChanged;
}
//注意:我没有处理NotifyCollectionChangedAction.Reset。
//您需要了解何时引发此事件并处理它
//以一种特殊的方式。
}
无效项\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
if(e.PropertyName.StartsWith(“ObjectA”))
{
//刷新任何相关视图、窗体、控件等。。。
}
}
}
假设您有一个表单1,在该表单中,您使用类R的实例来显示来自类a的实例列表。然后按“编辑”并将该类a的实例从类R实例发送到新表单
这将是对R实例中包含的对象的引用,因此将在form2中更新。您唯一需要做的就是刷新form1列表中类A的实例
解释:当您使用类的对象实例调用窗体或方法时,这将创建引用,而不是克隆,因此可以从第二个窗体2进行更新。一个nit,并不是所有对setter的调用都需要触发OnPropertyChanged
,通常您需要执行object.Equals(backingMember,value)
检查(因为您没有覆盖Equals(object)
这将是一个引用相等性检查,在这种情况下可以),看看属性是否真的被更改,然后再通知任何订阅者这个事实。@Scott同意,特别是因为重新绘制UI太慢了。不过,我可能不会更新示例代码,因为它已经非常冗长,而且每增加一行都会使其更难理解。在某些情况下,设置属性(即使值没有更改)可能会导致刷新。不完全是它的用途,但我以前见过。就我个人而言,我不会使用Object.Equals
,因为我们知道类型,所以EqualityComparer.Default.Equals(backingValue,value)
将是更好的选择。