我在WPF DataGrid中使用TrulyObservableCollection
作为数据源。我的类正确地实现了PropertyChange
事件(当属性发生变化时我收到通知)。 CollectionChanged
事件也会被触发。但是,我的问题在于PropertyChanged
事件和CollectionChanged
事件之间的关联。我可以在PropertyChanged
事件中看到哪个项目正在被更改(在这种情况下是sender
对象),但我似乎无法找到一种方法来查看哪个项目已从CollectionChanged
事件。 sender
对象是整个集合。在CollectionChanged
事件中查看哪个项目发生了变化的最佳方法是什么?相关的代码片段如下。感谢您的帮助,如果需要澄清,请告诉我。
设置集合的代码:
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
}
集合中使用的类:
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类
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);
}
}
修改
在item_PropertyChanged
事件中,NotifyCollectionChangedEventArgs
设置为NotifyCollectionChangedAction.Reset
。这会导致OldItems
和NewItems
为空,因此在这种情况下我无法获取更改的项目。我无法使用.Add
,因为Datagrid会使用其他项目进行更新。我无法让.Replace
工作以获取更改的项目。
答案 0 :(得分:3)
这个怎么样:
在包含ViewModel
ObservableCollection
的{{1}}中,ViewModel订阅bretItem
的{{1}}事件。
这样可以防止需要从CollectionChanged
派生的新类ObservableCollection
与其集合中的项目相关联。
在TrulyObservableCollection
的处理程序中,您可以像现在一样添加和删除ObservableCollection
事件处理程序。由于现在您的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)
}
}
您可能应考虑加载MVVM
并将dgBretList.ItemsSource = bretList;
dgBretList.Items.Refresh();
绑定到ViewModel
,而不是在View
的代码隐藏中编码逻辑。
答案 1 :(得分:2)
以这种方式使用集合更改事件是不合适的,因为它只是在从集合中添加/删除项目时才会被触发。这就是你撞墙的原因。你也有用这种方法打破Liskov替代原则的危险。
最好在集合类上实现INotifyPropertyChanged
接口,并在其中一个项触发其属性更改事件时触发该事件。