Replace the entire observable set with another ObservableCollection

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 (eg 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 all Items content with the return value of GetNewItems() without:

  • Breach of bindings.

  • Do I need to iterate over the elements and copy them one by one to another collection?

+6
source share
3 answers

You have several options:

  • Add INotifyPropertyChanged so that you can tell the user interface that the value of the elements has changed. This does not use the fact that INotifyCollectionChanged is implemented on an ObservableCollection. It will work, but it defeats the goal of using an ObservableCollection in the first place. This is not recommended, but it works.
  • Use the methods ObservableCollection Add / Remove / Modify / Update to change it together with the Dispatcher.
    • Note: without a dispatcher, you will get a NotSupportedException because CollectionViews do not support changes to the SourceCollection from a stream other than the Dispatcher stream.
  • Use the ObservableCollection Add / Remove / Modify / Update methods to modify it in conjunction with BindingOperations.EnableCollectionSynchronization . Recommended
    • Note. This is available only in .NET 4.5.
    • This is an alternative to using the dispatcher, avoiding the NotSupportedException.
    • Example

The numbers 2 and 3 in relation to your question, translate to cleaning the existing elements (Clear ()), and then add (Add ()) the elements returned in whatever way you want - see example for # 3. They indicate that cleaning and all additions must be done with the Manager (2) or by calling BindingOperations.EnableCollectionSynchronization . Good luck

Link: fooobar.com/questions/950832 / ...

+7
source

You can also create your own class that extends the ObservableCollection, here is an example of notifications sorting:

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

I use the simpler implementation above (I have not yet compared the notification aspect):

 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) { } } 

}

+2
source

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

 public void GetNewItems() { Items.Clear(); // some logic for getting some items from somewhere, and populating ret } 
+1
source

Source: https://habr.com/ru/post/950830/


All Articles