检测是否已修改ObservableCollection

时间:2012-12-18 15:47:14

标签: wpf observablecollection

我有一个嵌套在DockPanel中的DataGrid。 DockPanel用作数据上下文:

DockPanel1.DataContext = GetData();

GetData()方法返回一个ObservableCollection。

可以在DataGrid中以及嵌套在DockPanel中的一些文本框中修改ObservableCollection。我还使用DataView浏览集合。

我想检测集合是否已被修改,并在用户尝试关闭应用程序时警告用户而不保存数据。

是否有我可以使用的内置机制(集合或视图上的一种“IsDirty”标志)?如果没有,我想我必须监控所有控件并手动检测任何更改。

谢谢, 莱谢克

2 个答案:

答案 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会很好地做到这一点。