Replace Entire ObservableCollection with another ObservableCollection

最后都变了- 提交于 2019-12-30 06:27:26

问题


public class Alpha
{
    public ObservableCollection<Beta> Items { get; set; }

    public Alpha()
    {
        Items = new ObservableCollection<Beta>();
    }

    public void DoSomething()
    {
        Items = GetNewItems();  // whenever I do this, Items gets a new referene, 
                                // so every WPF binding (e.g. datagrids) are broken
    }

    public ObservableCollection<Beta> GetNewItems()
    {
         var ret = new ObservableCollection<Beta>();
         // some logic for getting some items from somewhere, and populating ret
         return ret;
    }
}

How can I replace the whole content of Items with the return value of GetNewItems() without:

  1. Breaking the bindings.

  2. Having to loop through the items and copy them one by one to the other collection ?


回答1:


You have some options:

  1. Implement INotifyPropertyChanged so you can inform the UI that the value of Items has changed. This does not utilize the fact that INotifyCollectionChanged is implemented on ObservableCollection. It will work, but it defeats the purpose of using an ObservableCollection in the first place. This is not recommended but it works.
    • Example
  2. Use the ObservableCollection's Add/Remove/Modify/Update methods to modify it in conjunction with the Dispatcher.
    • Note: without the Dispatcher, you will get a NotSupportedException because CollectionViews do not support changes to their SourceCollection from a thread different from the Dispatcher thread.
  3. Use the ObservableCollection's Add/Remove/Modify/Update methods to modify it in conjunction with BindingOperations.EnableCollectionSynchronization. Recommended
    • Note: This is only available in .NET 4.5.
    • This is an alternative to using the Dispatcher while avoiding the NotSupportedException.
    • Example

Numbers 2 and 3, with respect to your question, translate to clearing the existing items (Clear()) and then adding (Add()) the items returned by whatever method you want - see the example for #3. They key is that the clearing and all of the adding must be done with Dispatcher (2) or by calling BindingOperations.EnableCollectionSynchronization. Good luck!

Reference: Reed Copsey Answer - StackOverflow




回答2:


You can also make your own class that will extend the ObservableCollection, here is an example with notifications sorted:

https://github.com/jamesmontemagno/mvvm-helpers/blob/master/MvvmHelpers/ObservableRangeCollection.cs

I'm using simpler implementation of above (I did not compared the notification aspect yet):

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using BaseLibrary.Properties;

namespace BaseLibrary
{
/// <summary> 
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. 
/// </summary> 
/// <typeparam name="T"></typeparam> 
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    //INotifyPropertyChanged interited from ObservableCollection<T>
    #region INotifyPropertyChanged

    protected override event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion INotifyPropertyChanged

    /// <summary> 
    /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). 
    /// </summary> 
    public void AddRange(IEnumerable<T> collection)
    {
        if (collection == null) throw new ArgumentNullException(nameof(collection));

        foreach (var i in collection) Items.Add(i);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    /// <summary> 
    /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). 
    /// </summary> 
    public void RemoveRange(IEnumerable<T> collection)
    {
        if (collection == null) throw new ArgumentNullException(nameof(collection));

        foreach (var i in collection) Items.Remove(i);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    /// <summary> 
    /// Clears the current collection and replaces it with the specified item. 
    /// </summary> 
    public void Replace(T item)
    {
        Replace(new T[] { item });
    }

    /// <summary> 
    /// Replaces all elements in existing collection with specified collection of the ObservableCollection(Of T). 
    /// </summary> 
    public void Replace(IEnumerable<T> collection)
    {
        if (collection == null) throw new ArgumentNullException(nameof(collection));

        Items.Clear();
        foreach (var i in collection) Items.Add(i);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    /// <summary> 
    /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. 
    /// </summary> 
    public ObservableCollectionEx()
        : base() { }

    /// <summary> 
    /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. 
    /// </summary> 
    /// <param name="collection">collection: The collection from which the elements are copied.</param> 
    /// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception> 
    public ObservableCollectionEx(IEnumerable<T> collection)
        : base(collection) { }
}

}




回答3:


ObservableCollection implements INotifyCollectionChanged, which will update bindings whenever items get added or removed. All that is needed here is to clear the list for the CollectionChanged event to fire.

public void GetNewItems()
{
     Items.Clear();

     // some logic for getting some items from somewhere, and populating ret

}


来源:https://stackoverflow.com/questions/17996542/replace-entire-observablecollection-with-another-observablecollection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!