我有一个嵌套在DockPanel中的DataGrid。 DockPanel用作数据上下文:
DockPanel1.DataContext = GetData();
GetData()方法返回一个ObservableCollection。
可以在DataGrid中以及嵌套在DockPanel中的一些文本框中修改ObservableCollection。我还使用DataView浏览集合。
我想检测集合是否已被修改,并在用户尝试关闭应用程序时警告用户而不保存数据。
是否有我可以使用的内置机制(集合或视图上的一种“IsDirty”标志)?如果没有,我想我必须监控所有控件并手动检测任何更改。
谢谢, 莱谢克
答案 0 :(得分:3)
为了检测集合本身的变化,您必须附加CollectionChanged处理程序。如果还需要检测集合中包含的对象的更改,则必须将PropertyChanged处理程序附加到每个对象(前提是对象实现了INotifyPropertyChanged)。
实现基本上如下所示:
var collection = GetData();
collection.CollectionChanged += OnCollectionChanged;
...
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddPropertyChanged(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemovePropertyChanged(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Reset:
RemovePropertyChanged(e.OldItems);
AddPropertyChanged(e.NewItems);
break;
}
...
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
...
}
private void AddPropertyChanged(IEnumerable items)
{
if (items != null)
{
foreach (var obj in items.OfType<INotifyPropertyChanged>())
{
obj.PropertyChanged += OnPropertyChanged;
}
}
}
private void RemovePropertyChanged(IEnumerable items)
{
if (items != null)
{
foreach (var obj in items.OfType<INotifyPropertyChanged>())
{
obj.PropertyChanged -= OnPropertyChanged;
}
}
}
答案 1 :(得分:1)
详细说明Clemens上面的答案,这里是使用这些事件(在集合和包含的项目上)实现IsDirty标志的简单方法,如你所描述的:
public class DirtyCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
private bool isDirty = false;
public bool IsDirty
{
get { return this.isDirty; }
}
public void Clean()
{
this.isDirty = false;
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// We aren't concerned with how the collection changed, just that it did.
this.isDirty = true;
// But we do need to add the handlers to detect property changes on each item.
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this.AddPropertyChanged(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
this.RemovePropertyChanged(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Reset:
this.RemovePropertyChanged(e.OldItems);
this.AddPropertyChanged(e.NewItems);
break;
}
base.OnCollectionChanged(e);
}
private void AddPropertyChanged(IEnumerable items)
{
if (items != null)
{
foreach (var obj in items.OfType<INotifyPropertyChanged>())
{
obj.PropertyChanged += OnItemPropertyChanged;
}
}
}
private void RemovePropertyChanged(IEnumerable items)
{
if (items != null)
{
foreach (var obj in items.OfType<INotifyPropertyChanged>())
{
obj.PropertyChanged -= OnItemPropertyChanged;
}
}
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// A property of a contained item has changed.
this.isDirty = true;
}
}
代码应该是不言自明的。
当然,您可以删除“where T:INotifyPropertyChanged”以允许未实现该接口的对象存储在集合中,但之后您将不会收到有关它们的任何属性更改的通知,如如果没有该界面,他们就无法通知您。
如果您不仅想要跟踪集合是否脏,而且还要如何跟踪OnCollectionChanged和OnItemPropertyChanged中的一些新增内容来记录事件中传递的信息args会很好地做到这一点。