实现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
事件,并且假设对象被引发了。 这真的取决于你的要求。
INotifyCollectionChanged
与INotiftyPropertyChanged
不一样。 改变底层对象的属性并不意味着集合已经改变。
实现这种行为的一种方法是创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将通知视图。