ObservableCollection和Item PropertyChanged

我已经看到了很多关于这个问题的讨论,但也许我只是一个新手得到它。 如果我有一个可观察的集合,如msdn示例( http://msdn.microsoft.com/en-us/library/ms748365.aspx )中的“PersonNames”的集合,我得到更新我的视图,如果PersonName被添加或删除等等。当我更改PersonName的属性时,我想要更新我的视图。 就像我改变名字一样。 我可以为每个属性实现OnPropertyChanged ,并从INotifyPropertyChanged派生这个类似乎被调用的预期。 我的问题是,视图如何从ObservableCollection获取更新的数据,因为更改的属性不会导致ObservableCollection任何事件。 这可能很简单,但为什么我不能find一个令我惊喜的例子。 任何人都可以摆脱这个对我来说,或有任何指标,我将不胜感激它。 我们在当前的WPF应用程序的多个地方都有这种情况,并且正在努力解决这个问题。


“通常,负责显示数据的代码会向当前显示在屏幕上的每个对象添加一个PropertyChanged事件处理程序。”

有人可以给我一个这是什么意思的例子吗? 我的视图绑定到我有一个ObservableCollection ViewModel 。 此集合由具有支持PropertiesChanged事件的PropertiesChangedRowViewModel组成。 但我不知道如何使集合更新自己,所以我的观点将被更新。

以下是您将如何附加/分离到每个项目的PropertyChanged事件。

 ObservableCollection<INotifyPropertyChanged> items = new ObservableCollection<INotifyPropertyChanged>(); items.CollectionChanged += items_CollectionChanged; static void items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (INotifyPropertyChanged item in e.OldItems) item.PropertyChanged -= item_PropertyChanged; } if (e.NewItems != null) { foreach (INotifyPropertyChanged item in e.NewItems) item.PropertyChanged += item_PropertyChanged; } } static void item_PropertyChanged(object sender, PropertyChangedEventArgs e) { throw new NotImplementedException(); } 

我们在WPF聊天中写道:

 public class OcPropertyChangedListener<T> : INotifyPropertyChanged where T : INotifyPropertyChanged { private readonly ObservableCollection<T> _collection; private readonly string _propertyName; private readonly Dictionary<T, int> _items = new Dictionary<T, int>(new ObjectIdentityComparer()); public OcPropertyChangedListener(ObservableCollection<T> collection, string propertyName = "") { _collection = collection; _propertyName = propertyName ?? ""; AddRange(collection); CollectionChangedEventManager.AddHandler(collection, CollectionChanged); } private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: AddRange(e.NewItems.Cast<T>()); break; case NotifyCollectionChangedAction.Remove: RemoveRange(e.OldItems.Cast<T>()); break; case NotifyCollectionChangedAction.Replace: AddRange(e.NewItems.Cast<T>()); RemoveRange(e.OldItems.Cast<T>()); break; case NotifyCollectionChangedAction.Move: break; case NotifyCollectionChangedAction.Reset: Reset(); break; default: throw new ArgumentOutOfRangeException(); } } private void AddRange(IEnumerable<T> newItems) { foreach (T item in newItems) { if (_items.ContainsKey(item)) { _items[item]++; } else { _items.Add(item, 1); PropertyChangedEventManager.AddHandler(item, ChildPropertyChanged, _propertyName); } } } private void RemoveRange(IEnumerable<T> oldItems) { foreach (T item in oldItems) { _items[item]--; if (_items[item] == 0) { _items.Remove(item); PropertyChangedEventManager.RemoveHandler(item, ChildPropertyChanged, _propertyName); } } } private void Reset() { foreach (T item in _items.Keys.ToList()) { PropertyChangedEventManager.RemoveHandler(item, ChildPropertyChanged, _propertyName); _items.Remove(item); } AddRange(_collection); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void ChildPropertyChanged(object sender, PropertyChangedEventArgs e) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(sender, e); } private class ObjectIdentityComparer : IEqualityComparer<T> { public bool Equals(T x, T y) { return object.ReferenceEquals(x, y); } public int GetHashCode(T obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } } } public static class OcPropertyChangedListener { public static OcPropertyChangedListener<T> Create<T>(ObservableCollection<T> collection, string propertyName = "") where T : INotifyPropertyChanged { return new OcPropertyChangedListener<T>(collection, propertyName); } } 
  • 弱事件
  • 跟踪同一个项目被多次添加到集合中
  • 它泡沫的财产改变了孩子们的事件。
  • 静态类只是为了方便。

像这样使用它:

 var listener = OcPropertyChangedListener.Create(yourCollection); listener.PropertyChanged += (sender, args) => { //do you stuff} 

法案,

我相信你们现在已经find了解决方法或者解决scheme,但是我把这个发布给有这个常见问题的任何人。 您可以将此类replace为实现INotifyPropertyChanged的对象的集合ObservableCollections。 这是一种严厉的,因为它说,列表需要重置,而不是find一个属性/项目已经改变,但小列表的性能命中应该是不可能的。

渣子

 using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; namespace WCIOPublishing.Helpers { public class ObservableCollectionWithItemNotify<T> : ObservableCollection<T> where T: INotifyPropertyChanged { public ObservableCollectionWithItemNotify() { this.CollectionChanged += items_CollectionChanged; } public ObservableCollectionWithItemNotify(IEnumerable<T> collection) :base( collection) { this.CollectionChanged += items_CollectionChanged; foreach (INotifyPropertyChanged item in collection) item.PropertyChanged += item_PropertyChanged; } private void items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if(e != null) { if(e.OldItems!=null) foreach (INotifyPropertyChanged item in e.OldItems) item.PropertyChanged -= item_PropertyChanged; if(e.NewItems!=null) foreach (INotifyPropertyChanged item in e.NewItems) item.PropertyChanged += item_PropertyChanged; } } private void item_PropertyChanged(object sender, PropertyChangedEventArgs e) { var reset = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); this.OnCollectionChanged(reset); } } } 

正如你发现的那样,没有收集级别的事件表明集合中某个项目的属性已经改变。 通常,负责显示数据的代码为当前显示在屏幕上的每个对象添加一个PropertyChanged事件处理程序。

而不是ObservableCollection,只需使用BindingList <T>即可 。
下面的代码显示了DataGrid绑定到List和项目的属性。

 <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" > <DataGrid.Columns> <DataGridTextColumn Header="Values" Binding="{Binding Value}" /> </DataGrid.Columns> </DataGrid> </Window> 

 using System; using System.ComponentModel; using System.Windows; using System.Windows.Threading; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { var c = new BindingList<Data>(); this.DataContext = c; // add new item to list on each timer tick var t = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) }; t.Tick += (s, e) => { if (c.Count >= 10) t.Stop(); c.Add(new Data()); }; t.Start(); } } public class Data : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged = delegate { }; System.Timers.Timer t; static Random r = new Random(); public Data() { // update value on each timer tick t = new System.Timers.Timer() { Interval = r.Next(500, 1000) }; t.Elapsed += (s, e) => { Value = DateTime.Now.Ticks; this.PropertyChanged(this, new PropertyChangedEventArgs("Value")); }; t.Start(); } public long Value { get; private set; } } } 

以下代码给出了@Stack答案的一个简单的解释,并展示了如果BindingList有一个项目被更改并且显示ObservableCollection不会观察项目内部的更改。

 using System; using System.Collections.ObjectModel; using System.ComponentModel; namespace BindingListExample { class Program { public ObservableCollection<MyStruct> oc = new ObservableCollection<MyStruct>(); public System.ComponentModel.BindingList<MyStruct> bl = new BindingList<MyStruct>(); public Program() { oc.Add(new MyStruct()); oc.CollectionChanged += CollectionChanged; bl.Add(new MyStruct()); bl.ListChanged += ListChanged; } void ListChanged(object sender, ListChangedEventArgs e) { //Observe when the IsActive value is changed this event is triggered. Console.WriteLine(e.ListChangedType.ToString()); } void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { //Observe when the IsActive value is changed this event is not triggered. Console.WriteLine(e.Action.ToString()); } static void Main(string[] args) { Program pm = new Program(); pm.bl[0].IsActive = false; } } public class MyStruct : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private bool isactive; public bool IsActive { get { return isactive; } set { isactive = value; NotifyPropertyChanged("IsActive"); } } private void NotifyPropertyChanged(String PropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(PropertyName)); } } } }