实现CollectionChanged

我已经将CollectionChanged eventhandler(onCollectionChanged)添加到其中一个ObservableCollection属性。

我发现onCollectionChanged方法仅在添加项目或将项目移除到集合的情况下才会被调用,但是在收集项目被编辑的情况下不会被调用。

我想知道如何发送一个集合中新增,删除和编辑项目的列表/集合。

谢谢。

您必须为每个项目(必须实现INotifyPropertyChanged )添加一个PropertyChanged侦听器来获取有关编辑可观察列表中的对象的通知。

 public ObservableCollection<Item> Names { get; set; } public List<Item> ModifiedItems { get; set; } public ViewModel() { this.ModifiedItems = new List<Item>(); this.Names = new ObservableCollection<Item>(); this.Names.CollectionChanged += this.OnCollectionChanged; } void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach(Item newItem in e.NewItems) { ModifiedItems.Add(newItem); //Add listener for each item on PropertyChanged event newItem.PropertyChanged += this.OnItemPropertyChanged; } } if (e.OldItems != null) { foreach(Item oldItem in e.OldItems) { ModifiedItems.Add(oldItem); oldItem.PropertyChanged -= this.OnItemPropertyChanged; } } } void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e) { Item item = sender as Item; if(item != null) ModifiedItems.Add(item); } 

也许你必须检查一些项目是否已经在ModifedItems-List中(使用List的方法Contains(object obj)),并且只有当方法的结果为false时才添加一个新的项目。

Item必须实现INotifyPropertyChanged 。 看这个例子知道如何。 正如罗伯特·罗斯尼所说,如果你有这个需求,你也可以用IEditableObject来实现。

一个ItemsControl监听CollectionChanged来pipe理它在屏幕上呈现的项目集合的显示。 ContentControl监听PropertyChanged来pipe理它在屏幕上显示的特定项目的显示。 一旦理解了这一点,将两个概念分开放在心里很容易。

跟踪是否编辑项目不是这些接口之一。 属性更改不是编辑 – 也就是说,它们不一定代表某种用户启动的对对象状态的更改。 例如,一个对象可能有一个被计时器不断更新的ElapsedTime属性; UI需要被通知这些属性改变事件,但是它们当然不代表对象的基础数据的改变。

跟踪是否编辑对象的标准方法是首先使该对象实现IEditableObject 。 然后,您可以在对象的类的内部决定哪些更改构成编辑(即要求您调用BeginEdit )以及哪些更改不会。 然后可以实现一个布尔型的IsDirty属性,当BeginEdit被调用时被设置,当EndEdit或者CancelEdit被调用时被清除。 (我真的不明白为什么该属性不是IEditableObject一部分;我还没有实现一个不需要它的可编辑对象。

当然,如果你不需要实现第二个抽象层次的话,你不需要实现这个抽象层次 – 你肯定可以监听PropertyChanged事件,并且假设对象被引发了。 这真的取决于你的要求。

INotifyCollectionChangedINotiftyPropertyChanged不一样。 改变底层对象的属性并不意味着集合已经改变。

实现这种行为的一种方法是创build一个自定义集合,它将询问添加的对象并注册INotifyPropertyChanged.PropertyChanged事件; 那么当物品被移除时,它将需要适当的注销。

这种方法的一个警告是当你的对象嵌套N层深。 为了解决这个问题,你将需要使用reflection来实质性地询问每个属性,以确定它是否可能是另一个实现INotifyCollectionChanged或其他需要遍历的容器的集合。

这是一个基本未经testing的例子…

  public class ObservableCollectionExt<T> : ObservableCollection<T> { public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; protected override void SetItem(int index, T item) { base.SetItem(index, item); if(item is INotifyPropertyChanged) (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); } protected override void ClearItems() { for (int i = 0; i < this.Items.Count; i++) DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i])); base.ClearItems(); } protected override void InsertItem(int index, T item) { base.InsertItem(index, item); RegisterINotifyPropertyChanged(item); } protected override void RemoveItem(int index) { base.RemoveItem(index); DeRegisterINotifyPropertyChanged(index); } private void RegisterINotifyPropertyChanged(T item) { if (item is INotifyPropertyChanged) (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); } private void DeRegisterINotifyPropertyChanged(int index) { if (this.Items[index] is INotifyPropertyChanged) (this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged; } private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { T item = (T)sender; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item)); } } 

认为使用实现INotifyPropertyChanged项目填充ObservableCollection将导致CollectionChanged事件在项目引发PropertyChanged通知时触发。

第二个想法,我认为你需要使用BindingList<T>来获取单个项目的变化以这种方式传播开箱即用。

否则,您需要手动订阅每个项目的更改通知,并引发CollectionChanged事件。 请注意,如果您正在创build自己的派生ObservableCollection<T> ,则必须在实例化和Add()Insert()订阅,并在Remove()RemoveAt()Clear()上取消订阅。 否则,您可以订阅CollectionChanged事件,并使用事件参数中添加和删除的项目来订阅/取消订阅。

我编辑“ 这个答案 ”被拒绝! 所以我把我的编辑放在这里:

 void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach(Item newItem in e.NewItems) { ModifiedItems.Add(newItem); //Add listener for each item on PropertyChanged event if (e.Action == NotifyCollectionChangedAction.Add) newItem.PropertyChanged += this.ListTagInfo_PropertyChanged; else if (e.Action == NotifyCollectionChangedAction.Remove) newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged; } } // MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action. //if (e.OldItems != null) <--- removed } 

在winforms中, BindingList是标准的做法。 在WPF和Silverlight中,你通常坚持使用ObservableCollection并且需要监听每个项目的PropertyChanged

使用下面的代码:

我的型号:

  public class IceCream: INotifyPropertyChanged { private int liczba; public int Liczba { get { return liczba; } set { liczba = value; Zmiana("Liczba"); } } public IceCream(){} //in the same class implement the below-it will be responsible for track a changes public event PropertyChangedEventHandler PropertyChanged; private void Zmiana(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

而在我的class级PersonList实现方法中负责主动增加一个button后在AppBarControl中点击一个值

 async private void Add_Click(object sender, RoutedEventArgs e) { List<IceCream> items = new List<IceCream>(); foreach (IceCream item in IceCreamList.SelectedItems) { int i=Flavors.IndexOf(item); Flavors[i].Liczba =item.Liczba+ 1; //Flavors.Remove(item); //item.Liczba += 1; // items.Add(item); // Flavors.Add(item); } MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden"); d.Content = "Zwiększono liczbę o jeden"; await d.ShowAsync(); IceCreamList.SelectedIndex = -1; } } 

我希望有人能够注意到:

 private ObservableCollection<IceCream> Flavors; 

我发现这个限制最简单的解决scheme是使用RemoveAt(index)删除项目,然后使用InsertAt(index)在同一索引上添加修改的项目,因此ObservableCollection将通知视图。