仅在UI修改集合时处理CollectionChanged

时间:2013-12-03 17:33:15

标签: c# wpf observablecollection

我有一种情况,我想处理ObservableCollection的CollectionChanged事件,但只有在UI更改集合时才会这样。该集合绑定到DataGrid,我需要在用户添加或删除某些内容后检查一些内容。但是,当我从代码填充集合时,我不需要处理该事件。

我传统上处理这种方式的方法是在进行更改时解除事件。但这看起来有点像“hackish”。另一个想法是设置/取消设置一个类Boolean,它控制何时执行事件代码。但这似乎也很难看。

这种情况是否有任何模式或最佳做法?

3 个答案:

答案 0 :(得分:3)

这可以通过为ObservableCollection创建包装器来处理。它将实现修改集合所需的接口,它可以直接传递给包装的observable集合,但它将在调用基本方法时处理设置布尔值,并且当设置布尔值时不会触发其处理程序

这意味着你可以创建一个包装器,为事件添加一个处理程序,然后使用该包装器在代码中操作集合而不会触发事件,但仍然从UI获取事件(假设你绑定了UI)到底层集合,而不是包装器。

您可以通过添加ObservableCollection实现的一些其他接口(例如只读版本)来添加此功能。您可能还希望公开公开基础集合,如果您希望能够恢复“真实”集合。

这里的优点是,虽然布尔值的设置仍在进行,但您不再需要在整个代码中执行所有操作;它全都搬到了一个地方。

public class ObservableWrapper<T> : 
    ICollection<T>, IList<T>, INotifyCollectionChanged
{
    private ObservableCollection<T> other;
    private bool changing = false;
    public ObservableWrapper(ObservableCollection<T> wrapped)
    {
        other = wrapped;
        other.CollectionChanged += (sender, args) =>
        {
            var handler = CollectionChanged;
            if (handler != null && !changing)
                handler(sender, args);
        };
    }

    public void Add(T item)
    {
        changing = true;
        other.Add(item);
        changing = false;
    }

    public void Clear()
    {
        changing = true;
        other.Clear();
        changing = false;
    }

    public bool Contains(T item)
    {
        return other.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        other.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return other.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        changing = true;
        bool result = other.Remove(item);
        changing = false;
        return result;
    }

    public int IndexOf(T item)
    {
        return IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        changing = true;
        other.Insert(index, item);
        changing = false;
    }

    public void RemoveAt(int index)
    {
        changing = true;
        other.RemoveAt(index);
        changing = false;
    }

    public T this[int index]
    {
        get
        {
            return other[index];
        }
        set
        {
            changing = true;
            other[index] = value;
            changing = false;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return other.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return other.GetEnumerator();
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;
}

答案 1 :(得分:1)

由于您似乎有自定义要求,我建议您使用完全独立的列表来执行您需要的任何操作,而不是“黑客攻击”ObservableCollection。

让ObservableCollection成为ObservableCollection,并创建一个实现INotifyCollectionChanged和IList的类。

您需要覆盖添加,删除,等等方法,但最终您会拥有某种自己的智能集合,可以重复使用,它会按照您的要求进行。

您正在谈论的DataGrid需要知道您的自定义集合已实现IList接口,以便调用添加或删除方法。 DataGrid不会自动触发CollectionChanged事件。它只是调用那些add,remove,replace..etc方法,因此你需要在那些改变集合的方法中调用CollectionChanged明确。

这就是为什么CollectionChanged事件中的发送者始终是实例本身而不是某些wpf控件的原因。

答案 2 :(得分:-1)

对于许多其他活动,您可以查看sender object passed to your event handler,以查看它是DataGrid,普通UIElement还是您的ViewModel;例如:

yourObservableCollection.CollectionChanged +=
(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args) =>
{
    if (sender is System.Windows.Controls.DataGrid)
    {
        ...
    }
}