wpf 使用 TrulyObservableCollection 从 CollectionChanged 事件中获取更改的项目
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13401403/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get changed item from CollectionChanged event using TrulyObservableCollection
提问by jmgardn2
I'm using a TrulyObservableCollectionas a datasource in a WPF DataGrid. My class implements the PropertyChangeevent properly (I get notification when a property changes). The CollectionChangedevent gets triggered as well. However, my issue lies in the connection between the PropertyChangedevent and CollectionChangedevent. I can see in the PropertyChangedevent which item is being changed (in this case the senderobject), however I can't seem to find a way to see which one is changed from within the CollectionChangedevent. The senderobject is the whole collection. What's the best way to see which item has changed in the CollectionChangedevent? The relevant code snippets are below. Thank you for your help, and let me know if there needs to be some clarification.
我TrulyObservableCollection在 WPF DataGrid 中使用 a作为数据源。我的班级PropertyChange正确实施了该事件(当属性更改时我会收到通知)。该CollectionChanged事件被触发为好。但是,我的问题在于PropertyChanged事件与CollectionChanged事件之间的联系。我可以在PropertyChanged事件中看到哪个项目被更改(在这种情况下是sender对象),但是我似乎无法找到一种方法来查看从CollectionChanged事件中更改了哪个项目。该sender对象是整个集合。查看CollectionChanged事件中哪些项目已更改的最佳方法是什么?相关代码片段如下。感谢您的帮助,如果需要澄清,请告诉我。
Code for setting up the collection:
设置集合的代码:
private void populateBret()
{
bretList = new TrulyObservableCollection<BestServiceLibrary.bretItem>(BestClass.BestService.getBretList().ToList());
bretList.CollectionChanged += bretList_CollectionChanged;
dgBretList.ItemsSource = bretList;
dgBretList.Items.Refresh();
}
void bretList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//Do stuff here with the specific item that has changed
}
Class that is used in the collection:
集合中使用的类:
public class bretItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _blID;
public string _blGroup;
[DataMember]
public int blID
{
get { return _blID; }
set
{
_blID = value;
OnPropertyChanged("blID");
}
}
[DataMember]
public string blGroup
{
get { return _blGroup; }
set
{
_blGroup = value;
OnPropertyChanged("blGroup");
}
}
protected void OnPropertyChanged (String name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
TrulyObservableCollection class
TrulyObservableCollection 类
public class TrulyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public TrulyObservableCollection()
: base()
{
CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
}
public TrulyObservableCollection(List<T> list)
: base(list)
{
foreach (var item in list)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
}
void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
(item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
(item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(a);
}
}
EDIT:
编辑:
In the item_PropertyChangedevent the NotifyCollectionChangedEventArgsare set with NotifyCollectionChangedAction.Reset. This causes the OldItemsand NewItemsto be null, therefore I can't get the changed item in that case. I can't use .Addas the Datagrid is updated with an additional item. I can't appear to get .Replaceto work either to get the changed item.
在item_PropertyChanged事件中NotifyCollectionChangedEventArgs被设定NotifyCollectionChangedAction.Reset。这会导致OldItemsandNewItems为空,因此在这种情况下我无法获得更改的项目。我不能使用,.Add因为 Datagrid 更新了一个附加项目。我似乎无法开始.Replace工作以获取更改后的项目。
回答by Josh C.
How about this:
这个怎么样:
In your ViewModelthat contains the ObservableCollectionof bretItem, the ViewModel subscribes to the CollectionChangedevent of the ObservableCollection.
在ViewModel包含ObservableCollectionof 的您中bretItem,ViewModel 订阅了 的CollectionChanged事件ObservableCollection。
This will prevent the need of a new class TrulyObservableCollectionderived from ObservableCollectionthat is coupled to the items within its collection.
这将防止需要TrulyObservableCollection从其派生的新类ObservableCollection与其集合中的项目耦合。
Within the handler in your ViewModel, you can add and remove the PropertyChangedevent handler as you are now. Since it is now your ViewModelthat is being informed of the changes to objects within the collection, you can take the appropriate action.
在您的 处理程序中ViewModel,您可以PropertyChanged像现在一样添加和删除事件处理程序。由于现在是您ViewModel获悉集合中对象的更改,因此您可以采取适当的措施。
public class BretListViewModel
{
private void populateBret()
{
bretList = new ObservableCollection<BestServiceLibrary.bretItem>(BestClass.BestService.getBretList().ToList());
bretList.CollectionChanged += bretList_CollectionChanged;
}
void bretList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
(item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
(item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var bret = sender as bretItem;
//Update the database now!
//One note:
//The ObservableCollection raises its change event as each item changes.
//You should consider a method of batching the changes (probably using an ICommand)
}
}
A Thing of Note:
注意事项:
As an aside, it looks like you are breaking the MVVMpattern based upon this snippet:
顺便说一句,您似乎正在打破MVVM基于此代码段的模式:
dgBretList.ItemsSource = bretList;
dgBretList.Items.Refresh();
You probably should consider loading your ViewModeland binding your Viewto it instead of coding logic in the code-behind of your View.
您可能应该考虑加载您的ViewModel并将您的绑定View到它,而不是在View.
回答by Phil Gan
It's not appropriate to use the collection changed event in this way because it's only meant to be fired when adding/removing items from the collection. Which is why you've hit a wall. You're also in danger of breaking the Liskov substitution principle with this approach.
以这种方式使用集合更改事件是不合适的,因为它只是在从集合中添加/删除项目时才被触发。这就是你撞墙的原因。您也有使用这种方法违反 Liskov 替换原则的危险。
It's probably better to implement the INotifyPropertyChangedinterface on your collection class and fire that event when one of your items fires its property changed event.
最好INotifyPropertyChanged在您的集合类上实现接口并在您的项目之一触发其属性更改事件时触发该事件。

