【问题标题】:How to propagate property change notifications of objects within collections如何传播集合中对象的属性更改通知
【发布时间】:2013-12-30 19:30:46
【问题描述】:

假设我有这样的课程

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 的实例,另一个允许用户修改A 的实例,我需要R 的视图在用户更改@987654325 的任何实例时更改@。

如果用户更改A 实例的属性,传播该更改的最佳方式是什么(通过S 的实例),以便R 的所有实例显示A 的新状态?

【问题讨论】:

    标签: c# event-handling inotifypropertychanged windows-forms-designer


    【解决方案1】:

    编辑:修改此答案以更具体地解决问题,因为标签显示您已经知道INotifyPropertyChanged

    您需要在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));
        }
    }
    

    ...和班级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&lt;S&gt; 代替List&lt;S&gt;,并订阅其CollectionChanged 事件,并监视何时向listOfObjectS 添加或删除对象。添加后,订阅SPropertyChanged 事件。然后更新了R 的观点。示例:

    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...
            }
        }
    }
    

    【讨论】:

    • 一点,并不是所有对 setter 的调用都需要触发 OnPropertyChanged,通常你会想要做一个 Object.Equals(backingMember, value) 检查(因为你没有覆盖 Equals(object) 这将是一个引用相等性检查,在这种情况下很好)在通知任何订阅者该事实之前查看属性是否真的发生了变化。
    • @Scott 同意,尤其是。因为重绘 UI 太慢了。但是,我可能不会更新示例代码,因为它已经非常冗长,而且每添加一行都会使其更难消化。在某些情况下,可能需要设置属性(即使值没有更改)导致刷新。不完全是它的用途,但我以前见过它。就我个人而言,我不会使用Object.Equals,因为我们知道类型,所以EqualityComparer&lt;T&gt;.Default.Equals(backingValue, value) 会是更好的选择。
    【解决方案2】:

    假设您有一个 form1,您在其中使用类 R 的实例来显示类 A 的实例列表。然后按下编辑,然后将同一类 A 的实例从类 R 实例发送到新表单.

    这将是对 R 实例中包含的对象的引用,因此会在 form2 中更新。您唯一需要做的就是刷新 form1 列表中的类 A 的实例。

    解释一下:当您使用类的对象实例调用表单或方法时,这将创建一个引用,而不是克隆,因此可以从第二个表单更新。

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 2011-09-30
      • 2016-10-22
      • 1970-01-01
      • 2013-05-01
      • 2015-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多