Das Folgende ist eine Verbesserung gegenüber der von Jonathan gefundenen Implementierung. Zunächst wird jeder Ereignishandler auf dem ihm zugeordneten Dispatcher ausgeführt, anstatt davon auszugehen, dass sich alle auf demselben Dispatcher (UI) befinden. Zweitens wird BeginInvoke verwendet, damit die Verarbeitung fortgesetzt werden kann, während wir darauf warten, dass der Dispatcher verfügbar wird. Dies macht die Lösung in Situationen, in denen der Hintergrund-Thread viele Aktualisierungen mit der Verarbeitung zwischen den einzelnen Threads durchführt, viel schneller. Vielleicht noch wichtiger ist, dass es Probleme überwindet, die durch Blockieren während des Wartens auf den Aufruf verursacht werden (Deadlocks können beispielsweise auftreten, wenn WCF mit ConcurrencyMode.Single verwendet wird).
public class MTObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
Da wir BeginInvoke verwenden, ist es möglich, dass die zu benachrichtigende Änderung rückgängig gemacht wird, bevor der Handler aufgerufen wird. Dies würde normalerweise zu einem "Index außerhalb des Bereichs" führen. Ausnahme wird ausgelöst, wenn die Ereignisargumente mit dem neuen (geänderten) Status der Liste verglichen werden. Um dies zu vermeiden, werden alle verzögerten Ereignisse durch Reset-Ereignisse ersetzt. Dies kann in einigen Fällen zu übermäßigem Neuzeichnen führen.