Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There are two possible approaches. The first would be to inherit from a concurrent collection and add INotifyCollectionChanged functionality, and the second would be to inherit from a collection that implements INotifyCollectionChanged and add concurrency support. I think it is far easier and safer to add INotifyCollectionChanged support to a concurrent collection. My suggestion is below.</p> <p>It looks long but most of the methods just call the internal concurrent collection as if the caller were using it directly. The handful of methods that add or remove from the collection inject a call to a private method that raises the notification event on the dispatcher provided at construction, thus allowing the class to be thread safe but ensuring the notifications are raised on the same thread all the time.</p> <pre><code>using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Windows.Threading; namespace Collections { /// &lt;summary&gt; /// Concurrent collection that emits change notifications on a dispatcher thread /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of objects in the collection&lt;/typeparam&gt; [Serializable] [ComVisible(false)] [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)] public class ObservableConcurrentBag&lt;T&gt; : IProducerConsumerCollection&lt;T&gt;, IEnumerable&lt;T&gt;, ICollection, IEnumerable { /// &lt;summary&gt; /// The dispatcher on which event notifications will be raised /// &lt;/summary&gt; private readonly Dispatcher dispatcher; /// &lt;summary&gt; /// The internal concurrent bag used for the 'heavy lifting' of the collection implementation /// &lt;/summary&gt; private readonly ConcurrentBag&lt;T&gt; internalBag; /// &lt;summary&gt; /// Initializes a new instance of the ConcurrentBag&lt;T&gt; class that will raise &lt;see cref="INotifyCollectionChanged"/&gt; events /// on the specified dispatcher /// &lt;/summary&gt; public ObservableConcurrentBag(Dispatcher dispatcher) { this.dispatcher = dispatcher; this.internalBag = new ConcurrentBag&lt;T&gt;(); } /// &lt;summary&gt; /// Initializes a new instance of the ConcurrentBag&lt;T&gt; class that contains elements copied from the specified collection /// that will raise &lt;see cref="INotifyCollectionChanged"/&gt; events on the specified dispatcher /// &lt;/summary&gt; public ObservableConcurrentBag(Dispatcher dispatcher, IEnumerable&lt;T&gt; collection) { this.dispatcher = dispatcher; this.internalBag = new ConcurrentBag&lt;T&gt;(collection); } /// &lt;summary&gt; /// Occurs when the collection changes /// &lt;/summary&gt; public event NotifyCollectionChangedEventHandler CollectionChanged; /// &lt;summary&gt; /// Raises the &lt;see cref="CollectionChanged"/&gt; event on the &lt;see cref="dispatcher"/&gt; /// &lt;/summary&gt; private void RaiseCollectionChangedEventOnDispatcher(NotifyCollectionChangedEventArgs e) { this.dispatcher.BeginInvoke(new Action&lt;NotifyCollectionChangedEventArgs&gt;(this.RaiseCollectionChangedEvent), e); } /// &lt;summary&gt; /// Raises the &lt;see cref="CollectionChanged"/&gt; event /// &lt;/summary&gt; /// &lt;remarks&gt; /// This method must only be raised on the dispatcher - use &lt;see cref="RaiseCollectionChangedEventOnDispatcher" /&gt; /// to do this. /// &lt;/remarks&gt; private void RaiseCollectionChangedEvent(NotifyCollectionChangedEventArgs e) { this.CollectionChanged(this, e); } #region Members that pass through to the internal concurrent bag but also raise change notifications bool IProducerConsumerCollection&lt;T&gt;.TryAdd(T item) { bool result = ((IProducerConsumerCollection&lt;T&gt;)this.internalBag).TryAdd(item); if (result) { this.RaiseCollectionChangedEventOnDispatcher(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } return result; } public void Add(T item) { this.internalBag.Add(item); this.RaiseCollectionChangedEventOnDispatcher(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } public bool TryTake(out T item) { bool result = this.TryTake(out item); if (result) { this.RaiseCollectionChangedEventOnDispatcher(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item)); } return result; } #endregion #region Members that pass through directly to the internal concurrent bag public int Count { get { return this.internalBag.Count; } } public bool IsEmpty { get { return this.internalBag.IsEmpty; } } bool ICollection.IsSynchronized { get { return ((ICollection)this.internalBag).IsSynchronized; } } object ICollection.SyncRoot { get { return ((ICollection)this.internalBag).SyncRoot; } } IEnumerator&lt;T&gt; IEnumerable&lt;T&gt;.GetEnumerator() { return ((IEnumerable&lt;T&gt;)this.internalBag).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this.internalBag).GetEnumerator(); } public T[] ToArray() { return this.internalBag.ToArray(); } void IProducerConsumerCollection&lt;T&gt;.CopyTo(T[] array, int index) { ((IProducerConsumerCollection&lt;T&gt;)this.internalBag).CopyTo(array, index); } void ICollection.CopyTo(Array array, int index) { ((ICollection)this.internalBag).CopyTo(array, index); } #endregion } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload