1

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

DockPanel1.DataContext = GetData();

GetData() 方法返回一个 ObservableCollection。

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

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

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

谢谢, 莱斯泽克

4

2 回答 2

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;
        }
    }
}
于 2012-12-18T15:56:47.947 回答
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 中传递的信息的添加会很好地做到这一点。

于 2012-12-18T16:51:22.433 回答