如何确定ObservableCollection的更改源

时间:2017-03-23 11:39:27

标签: c# collections

我有两个ObservableCollection通过相互匹配CollectionChanged事件来监听彼此之间的变化的情况。目标是使两个集合保持同步。

我如何区分

  • 来自第三方的外部更改,即因为某些代码更改(添加,删除,...)其中一个集合,
  • 由于同步过程而发生的“内部更改”(即为了跟上同步而更改其中一个集合)。

如果我不区分这两者?我想到了暂时的同步过程。但是,这将阻止我在同步(多线程)期间收到有关外部更改的通知。所以我想这一切都可以解决变化的起源问题。

1 个答案:

答案 0 :(得分:0)

如果你不反对扩展ObservableCollection类,你可以这样做:

public class SynchronizingCollection<T> : ObservableCollection<T>
{
    // the second, synchroniozed collection
    SynchronizingCollection<T> _synchronizedCollection;
    // field used to determine if the synchronization is already in progress
    bool _isSynchronizing = false;

    // Methods AddRange, RemoveItems, ReplaceAll, ClearAll
    // prevent CollectionChanged event from being rised unnecessarily
    // and allow for adding/clearing/replacing items in bulk
    public void AddRange(IEnumerable<T> range)
    {
        if (range == null)
            throw new ArgumentNullException("range");

        foreach (T item in range)
        {
            this.Items.Add(item);
        }

        this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (IList)range.ToList()));
    }

    public void ReplaceAll(IEnumerable<T> range)
    {
        if (range == null)
            throw new ArgumentNullException("range");

        this.Items.Clear();
        foreach (T item in range)
        {
            this.Items.Add(item);
        }

        this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public void RemoveItems(IEnumerable<T> range)
    {

        if (range == null)
            throw new ArgumentNullException("range");

        foreach (T item in range)
        {
            this.Items.Remove(item);
        }

        this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (IList)range.ToList()));
    }

    public void ClearAll()
    {
        IList old = this.Items.ToList();
        base.Items.Clear();
        this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, old));
    }

    // assigning the second collection as synchronized
    public void SetSynchronizedCollection(SynchronizingCollection<T> synchronized)
    {
        this._synchronizedCollection = synchronized;
        this._synchronizedCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(_synchronized_CollectionChanged);
        this.CollectionChanged += new NotifyCollectionChangedEventHandler(this_CollectionChanged);
    }

    void this_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (_isSynchronizing || _synchronizedCollection==null) return;
        _isSynchronizing = true;

        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                _synchronizedCollection.AddRange(e.NewItems.OfType<T>());
                break;
            case NotifyCollectionChangedAction.Move:
                // implement...
                break;
            case NotifyCollectionChangedAction.Remove:
                _synchronizedCollection.RemoveItems(e.OldItems.OfType<T>());
                break;
            case NotifyCollectionChangedAction.Replace:
                // implement...
                break;
            case NotifyCollectionChangedAction.Reset:
                _synchronizedCollection.ReplaceAll(this.Items);
                break;
        }

        _isSynchronizing = false;
    }

    void _synchronized_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (_isSynchronizing) return;
        _isSynchronizing = true;


        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                this.AddRange(e.NewItems.OfType<T>());
                break;
            case NotifyCollectionChangedAction.Move:
                // implement...
                break;
            case NotifyCollectionChangedAction.Remove:
                this.RemoveItems(e.OldItems.OfType<T>());
                break;
            case NotifyCollectionChangedAction.Replace:
                // implement...
                break;
            case NotifyCollectionChangedAction.Reset:
                this.ReplaceAll(_synchronizedCollection.Items);
                break;
        }

        _isSynchronizing = false;
    }
}

使用代码:

            SynchronizingCollection<string> c1 = new SynchronizingCollection<string>();
            SynchronizingCollection<string> c2 = new SynchronizingCollection<string>();

            c1.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(c1_CollectionChanged);
            c2.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(c2_CollectionChanged);
            c1.SetSynchronizedCollection(c2);

            c1.AddRange(new string[] { "d", "s" });
            c1.ClearAll();
            c1.Add("a");
            c1.Add("b");
            c1.Add("c");
            c1.Add("d");

            c2.Remove("c");

            List<string> l = new List<string> { "x1", "x2", "x3", "x4", "x5" };

            c2.AddRange(l);
            c1.RemoveItems(new string[] { "x2", "b" });
            c2.ReplaceAll(l);
//....

    void c1_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Console.WriteLine(e.Action + " Collection 1 changed");
    }
    void c2_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Console.WriteLine(e.Action + " Collection 2 changed");
    }