using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Threading;
using System.Collections.Specialized;
using System.ComponentModel;
namespace TsViewer
{
///
/// ObservableCollection that automatically calls event handlers via the target object's Dispatcher if available.
/// Targets that don't derive from DispatcherObject are called on the same thread that the event is invoked from.
///
/// Author: Jason S. Clary
///
/// The type of the elements
public class DispatchingObservableCollection : ObservableCollection
{
private readonly EventHandlerList _delegates = new EventHandlerList();
private readonly Dictionary _dispatcherDelegates =
new Dictionary();
public DispatchingObservableCollection() { }
public DispatchingObservableCollection(IEnumerable collection) : base(collection) { }
private readonly object _collectionChanged = new Object();
public override event NotifyCollectionChangedEventHandler CollectionChanged
{
add { AddEventHandler(_collectionChanged, value); }
remove { RemoveEventHandler(_collectionChanged, value); }
}
private readonly object _propertyChanged = new Object();
protected override event PropertyChangedEventHandler PropertyChanged
{
add { AddEventHandler(_propertyChanged, value); }
remove { RemoveEventHandler(_propertyChanged, value); }
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var threadAgnosticDelegates = (NotifyCollectionChangedEventHandler)_delegates[_collectionChanged];
if (threadAgnosticDelegates != null) threadAgnosticDelegates(this, e);
foreach (var dispatcherDelegatePair in _dispatcherDelegates)
{
var eventHandler =
(NotifyCollectionChangedEventHandler)dispatcherDelegatePair.Value[_collectionChanged];
if (eventHandler == null) continue;
if (dispatcherDelegatePair.Key.CheckAccess())
eventHandler(this, e);
else
dispatcherDelegatePair.Key.BeginInvoke(DispatcherPriority.DataBind, new Action(() => eventHandler(this, e)));
}
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
var threadAgnosticDelegates = (PropertyChangedEventHandler)_delegates[_propertyChanged];
if (threadAgnosticDelegates != null) threadAgnosticDelegates(this, e);
foreach (var dispatcherDelegatePair in _dispatcherDelegates)
{
var eventHandler =
(PropertyChangedEventHandler)dispatcherDelegatePair.Value[_propertyChanged];
if (eventHandler == null) continue;
if (dispatcherDelegatePair.Key.CheckAccess())
eventHandler(this, e);
else
dispatcherDelegatePair.Key.BeginInvoke(DispatcherPriority.DataBind, new Action(() => eventHandler(this, e)));
}
}
private void AddEventHandler(object eventObject, Delegate handler)
{
var target = handler.Target as DispatcherObject;
if (target != null)
{
EventHandlerList eventHandlerList;
if (!_dispatcherDelegates.TryGetValue(target.Dispatcher, out eventHandlerList))
{
eventHandlerList = new EventHandlerList();
_dispatcherDelegates.Add(target.Dispatcher, eventHandlerList);
}
eventHandlerList.AddHandler(eventObject, handler);
}
else
_delegates.AddHandler(eventObject, handler);
}
private void RemoveEventHandler(object eventObject, Delegate handler)
{
var target = handler.Target as DispatcherObject;
if (target != null)
{
EventHandlerList eventHandlerList;
if (!_dispatcherDelegates.TryGetValue(target.Dispatcher, out eventHandlerList))
{
eventHandlerList = new EventHandlerList();
_dispatcherDelegates.Add(target.Dispatcher, eventHandlerList);
}
eventHandlerList.RemoveHandler(eventObject, handler);
}
else
_delegates.RemoveHandler(eventObject, handler);
}
}
}