【问题标题】:Replace Entire ObservableCollection with another ObservableCollection用另一个 ObservableCollection 替换整个 ObservableCollection
【发布时间】:2013-08-02 12:32:52
【问题描述】:
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;
    }
}

如何将Items的全部内容替换为GetNewItems()的返回值而没有:

  1. 打破绑定。

  2. 必须循环遍历项目并将它们一一复制到另一个集合?

【问题讨论】:

  • 嘿@user2270404,这是在 .NET 4.5 中完成的吗?
  • @The Red Lou:不,它是在 4.0 中完成的
  • 好吧,我只是想让您知道,如果您愿意看一下,我会根据反馈更新我的答案。我找到了一些其他的方法来完成它并做了例子,但是第三种需要.NET 4.5。
  • @The Red Lou:很好,谢谢!

标签: c# wpf binding observablecollection


【解决方案1】:

你有一些选择:

  1. 实现 INotifyPropertyChanged,以便通知 UI Items 的值已更改。这没有利用在 ObservableCollection 上实现 INotifyCollectionChanged 的​​事实。它会起作用,但它首先破坏了使用 ObservableCollection 的目的。不建议这样做,但它有效。
  2. 使用 ObservableCollection 的 Add/Remove/Modify/Update 方法与 Dispatcher 一起修改它。
    • 注意:如果没有 Dispatcher,您将收到 NotSupportedException,因为 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 SourceCollection
  3. 结合BindingOperations.EnableCollectionSynchronization 使用 ObservableCollection 的 Add/Remove/Modify/Update 方法来修改它。 推荐
    • 注意:这仅在 .NET 4.5 中可用。
    • 这是在避免 NotSupportedException 的同时使用 Dispatcher 的替代方法。
    • Example

关于您的问题,数字 2 和 3 转换为清除现有项目 (Clear()),然后添加 (Add()) 您想要的任何方法返回的项目 - 请参见 #3 的示例。他们的关键是必须使用 Dispatcher (2) 或调用 BindingOperations.EnableCollectionSynchronization 来完成清除和所有添加。 祝你好运!

参考:Reed Copsey Answer - StackOverflow

【讨论】:

    【解决方案2】:

    您也可以创建自己的类来扩展 ObservableCollection,这是一个对通知进行排序的示例:

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

    我正在使用上述更简单的实现(我还没有比较通知方面):

    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 实现了 INotifyCollectionChanged,它会在添加或删除项目时更新绑定。这里需要做的就是清除要触发的 CollectionChanged 事件的列表。

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

      【讨论】:

      • 注意:这可能导致 NotSupportedException。
      • @TheRedLou 你能解释一下吗?我不知道这一点。
      • @TheRedLou 这个问题没有任何关于非 ui 线程的内容。
      • @TheRedLou 这个问题在哪里说我们正在处理一个非 ui 线程?
      • @TheRedLou 你说的毫无意义。您需要返回并查看非 ui 线程和其他基本原则意味着什么。基于此,您不应该对一个完全好的答案投反对票。
      猜你喜欢
      • 1970-01-01
      • 2017-05-24
      • 2021-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-23
      • 1970-01-01
      • 2012-10-19
      相关资源
      最近更新 更多