Is there a Threadsafe Observable collection in .NET 4?

前端 未结 4 1762
南方客
南方客 2020-12-03 01:45

Platform: WPF, .NET 4.0, C# 4.0

Problem: In the Mainwindow.xaml i have a ListBox bound to a Customer collection which is currently an ObservableCollecti

4条回答
  •  被撕碎了的回忆
    2020-12-03 01:51

    Please take a look at the BindableCollection from Caliburn.Micro library:

    /// 
    /// A base collection class that supports automatic UI thread marshalling.
    /// 
    /// The type of elements contained in the collection.
    #if !SILVERLIGHT && !WinRT
    [Serializable]
    #endif
    public class BindableCollection : ObservableCollection, IObservableCollection {
    
        /// 
        ///   Initializes a new instance of the  class.
        /// 
        public BindableCollection() {
            IsNotifying = true;
        }
    
        /// 
        ///   Initializes a new instance of the  class.
        /// 
        /// The collection from which the elements are copied.
        /// 
        ///   The  parameter cannot be null.
        /// 
        public BindableCollection(IEnumerable collection) : base(collection) {
            IsNotifying = true;
        }
    
    #if !SILVERLIGHT && !WinRT
        [field: NonSerialized]
    #endif
        bool isNotifying; //serializator try to serialize even autogenerated fields
    
        /// 
        ///   Enables/Disables property change notification.
        /// 
    #if !WinRT
        [Browsable(false)]
    #endif
        public bool IsNotifying {
            get { return isNotifying; }
            set { isNotifying = value; }
        }
    
        /// 
        ///   Notifies subscribers of the property change.
        /// 
        /// Name of the property.
    #if WinRT || NET45
        public virtual void NotifyOfPropertyChange([CallerMemberName]string propertyName = "") {
    #else
        public virtual void NotifyOfPropertyChange(string propertyName) {
    #endif
            if(IsNotifying)
                Execute.OnUIThread(() => OnPropertyChanged(new PropertyChangedEventArgs(propertyName)));
        }
    
        /// 
        ///   Raises a change notification indicating that all bindings should be refreshed.
        /// 
        public void Refresh() {
            Execute.OnUIThread(() => {
                OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            });
        }
    
        /// 
        ///   Inserts the item to the specified position.
        /// 
        /// The index to insert at.
        /// The item to be inserted.
        protected override sealed void InsertItem(int index, T item) {
            Execute.OnUIThread(() => InsertItemBase(index, item));
        }
    
        /// 
        ///   Exposes the base implementation of the  function.
        /// 
        /// The index.
        /// The item.
        /// 
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// 
        protected virtual void InsertItemBase(int index, T item) {
            base.InsertItem(index, item);
        }
    
    #if NET || WP8 || WinRT
    /// 
    /// Moves the item within the collection.
    /// 
    /// The old position of the item.
    /// The new position of the item.
        protected sealed override void MoveItem(int oldIndex, int newIndex) {
            Execute.OnUIThread(() => MoveItemBase(oldIndex, newIndex));
        }
    
        /// 
        /// Exposes the base implementation fo the  function.
        /// 
        /// The old index.
        /// The new index.
        /// Used to avoid compiler warning regarding unverificable code.
        protected virtual void MoveItemBase(int oldIndex, int newIndex) {
            base.MoveItem(oldIndex, newIndex);
        }
    #endif
    
        /// 
        ///   Sets the item at the specified position.
        /// 
        /// The index to set the item at.
        /// The item to set.
        protected override sealed void SetItem(int index, T item) {
            Execute.OnUIThread(() => SetItemBase(index, item));
        }
    
        /// 
        ///   Exposes the base implementation of the  function.
        /// 
        /// The index.
        /// The item.
        /// 
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// 
        protected virtual void SetItemBase(int index, T item) {
            base.SetItem(index, item);
        }
    
        /// 
        ///   Removes the item at the specified position.
        /// 
        /// The position used to identify the item to remove.
        protected override sealed void RemoveItem(int index) {
            Execute.OnUIThread(() => RemoveItemBase(index));
        }
    
        /// 
        ///   Exposes the base implementation of the  function.
        /// 
        /// The index.
        /// 
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// 
        protected virtual void RemoveItemBase(int index) {
            base.RemoveItem(index);
        }
    
        /// 
        ///   Clears the items contained by the collection.
        /// 
        protected override sealed void ClearItems() {
            Execute.OnUIThread(ClearItemsBase);
        }
    
        /// 
        ///   Exposes the base implementation of the  function.
        /// 
        /// 
        ///   Used to avoid compiler warning regarding unverifiable code.
        /// 
        protected virtual void ClearItemsBase() {
            base.ClearItems();
        }
    
        /// 
        ///   Raises the  event with the provided arguments.
        /// 
        /// Arguments of the event being raised.
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
            if (IsNotifying) {
                base.OnCollectionChanged(e);
            }
        }
    
        /// 
        ///   Raises the PropertyChanged event with the provided arguments.
        /// 
        /// The event data to report in the event.
        protected override void OnPropertyChanged(PropertyChangedEventArgs e) {
            if (IsNotifying) {
                base.OnPropertyChanged(e);
            }
        }
    
        /// 
        ///   Adds the range.
        /// 
        /// The items.
        public virtual void AddRange(IEnumerable items) {
            Execute.OnUIThread(() => {
                var previousNotificationSetting = IsNotifying;
                IsNotifying = false;
                var index = Count;
                foreach(var item in items) {
                    InsertItemBase(index, item);
                    index++;
                }
                IsNotifying = previousNotificationSetting;
    
                OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            });
        }
    
        /// 
        ///   Removes the range.
        /// 
        /// The items.
        public virtual void RemoveRange(IEnumerable items) {
            Execute.OnUIThread(() => {
                var previousNotificationSetting = IsNotifying;
                IsNotifying = false;
                foreach(var item in items) {
                    var index = IndexOf(item);
                    if (index >= 0) {
                        RemoveItemBase(index);
                    }
                }
                IsNotifying = previousNotificationSetting;
    
                OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            });
        }
    
        /// 
        /// Called when the object is deserialized.
        /// 
        /// The streaming context.
        [OnDeserialized]
        public void OnDeserialized(StreamingContext c) {
            IsNotifying = true;
        }
    
        /// 
        /// Used to indicate whether or not the IsNotifying property is serialized to Xml.
        /// 
        /// Whether or not to serialize the IsNotifying property. The default is false.
        public virtual bool ShouldSerializeIsNotifying() {
            return false;
        }
    }
    

    Source

    PS. Just take in mind that this class use some other classes from Caliburn.Micro so that you could either copy/pase all dependencies by your-self - OR - if you are not using any other application frameworks - just reference the library binary and give it a chance.

提交回复
热议问题