Feedbook is a online RSS Reader and social networking for windows, visit website for more info …
Problem Statement:
One of the issues we face in WPF data binding that we cannot update ObservableCollection<T> from other thread (cross thread), other wise we will get an exception “This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread”. So we cannot leverage from any asynchronous operation. Winforms and WPF controls required single thread apartment to render itself and it also check for cross thread validation. In this article not only we will solved this exception problem, but also fix one memory leak in .NET.
Note:
a) You can download complete visual studio 9 solution from here
b) All classes are available in open source class library at CodePlex.com : http://coresystem.codeplex.com
c) See Feedbook RSS Reader supports any RSS and Atom news feed, it is also a twitter client and podcasts downloader which heavily use ObservableCollection. It is build using CoreSystem Library and CrystalMapper ORM
Briefings and Solution:
When ever in WPF a collection class implements INotifyCollectionChanged or any class that implement INotifyPropertyChanged interfaces, they can be use in data binding for automatic updating target property, however data binding requires target property to be DependencyProperty . You should heard the term dependency property a lot in WPF. Why data binding in WPF needs target property to be dependency property? Well there are many good reasons for that. One is the way value assigned to dependency properties does matters. You will amaze to know that not all values assign through data binding get reflected. I will not go into details because that is not my objective over here but here is a hint: If you ever set a dependency property, let say “Background” in Style through setters for specific <Style TargetType=”{x:Type Button}”> type Button and then for certain Button down the logical tree you reset Button.Background property, then style property will not be applied. dah! Well you will be thinking that Style is set before explicit local value assigned. May be you are write but the thing is that if you were in WPF team, would you rely on user to maintain sequence in declarative language, probably not. So WPF team implemented dependency property and data binding in a way that “local value” (explicitly assigned value to property of FrameworkElement object) has higher priority over “style setters value” therefore you local value override the style value. There are at least seven to eight different source classifications on priority level. If you want to learn WPF look at this great book WPF Unleashed.
Cause of Problem:
Forget CollectionView mentioned in exception for a second because you might get confused. Every UI Controls that registers its handlers to publishers like ObservableCollection<T>, when handlers get fired on every subscribers, in this case UI Controls! take certain action on it to keep itself updated. Now lets take ListBox (WPF one) it exposes ItemsSource dependency property, when we bind a collection to it ( <ListBox ItemsSource=”{Binding Source={StaticResource myCollection}}” /> myCollection defined some where in window or application resources or higher in visual tree), it registers its handlers since INotifyCollectionChanged interface is implemented by ObservableCollection<T> to track collection changed. If an item is add on collection in a call to “collection.Add(myItem)” function. This add function not only add an item to collection but also raise a CollectionChanged event in this “Add” function call, that raise event function invoke all handlers internally, and execute them one by one on the bases of FCFS algorithm in same thread. After all handlers got executed then finally “Add” function returns. Problem arises if this whole operation is under a thread that is different from UI thread. In this case handlers attached by ListBox internally take action on added “myItem” and tries to update itself, at this point call to thread validation by ListBox fails.
Solution to the Problem:
We can solved this problem by firing handler(s) in same UI thread. But wait a minute, how we gonna known that in which UI thread to fire a specific handler(s) register by certain control, there can be multiple different UI threads and multiple controls from different thread register there handlers. if we some how able to fire handlers in one particular UI thread, the exception will still going to be raise. As we know that when a UI control registers its handler, it will always register in its own thread, at the time of registration if we simply store registering thread information along with handler, then later on we can fire each handler in there respective threads. For instance some thing like this:
public override event NotifyCollectionChangedEventHandler CollectionChanged { add { //base.CollectionChanged += value; this.handlersThread.Add(value, Thread.CurrentThread); //We know that Current thread must be UI thread //of that specific control other while expection //will be thrown at registering time. } remove { //base.CollectionChanged -= value; this.handlersThread.Remove(value); } }
Where value is a .NET special class System.Delegate. When firing handlers we simply override OnCollectionChange method something like this:
protected override voidOnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
//base.OnCollectionChanged(e);
foreach(Delegatehandler in this.handlersThread.Keys)
{
Dispatcherdispatcher = Dispatcher.FromThread(this.handlersThread[handler]);
dispatcher.Invoke(DispatcherPriority.Send, handler, this, e);
}
}
.NET Memory Leak:
Above solution works fine so far as we need it to work transparently. I have told you that value here is nothing but System.Delegate class object. When ever a objects subscribe to some publishers for respond, publisher holds reference of subscriber in order to respond back when particular event occurred. System.Delegate.Target property holds this reference of subscriber. This action produces memory leak, for example you create an object of certain class and subscribe its public collection changed event like:
public void MemoryLeakTest() { ObservableCollection<Object> myCollection = new ObservableCollection<object>(); DummSubscriber subscriber = new DummSubscriber(); // Holds reference to an object but allow it to garbage collected WeakReference subcriberRef = new WeakReference(subscriber); myCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(subscriber.CollectionChanged); // Setting null so that it can be collected by GC subscriber = null; // Calling GC so that object could be collected GC.WaitForPendingFinalizers(); // Checking for subcriber refereced; if (null != subcriberRef.Target) Debugger.Break(); //Conditionaly allow breaking in debug mode. }
It will certainly break at “Debugger.Break();” because value of type System.Delegate.Target certainly holds subscriber reference. Bad thing here is you lost object reference, therefore it is going to be reside in memory and you cannot unsubscribe it anymore.
Memory Leak Solution:
To find solution for this problem we need to do some work and make classes so that the code will be clean and those classes can be used by any other Publisher object who’s subscribers are UI controls and memory leak will be handled gracefully. Lets go to implementation, we will have two classes DispatchEvent and DispatchEvent.DispatchHandler, DispatchHandler is private class in DispatchEvent. DispatchEvent exposes three major functions Add, Remove and Fire, all these function are replacement of event object in .NET. Our DispatchEvent contains a List<DispatchHandler> handlerList private member to holds all subscribers, where DispatchHandler represent each subscriber. Now lets see what DispatchHandler is:
private class DispatchHandler : IDisposable { private MethodInfo handlerInfo; private WeakReference targetRef; private WeakReference dispatcherRef; public DispatchHandler(Delegate handler, Dispatcher dispatcher) { this.handlerInfo = handler.Method; this.targetRef = new WeakReference(handler.Target); this.dispatcherRef = new WeakReference(dispatcher); } public bool IsDisposable { get { object target = this.Target; Dispatcher dispatcher = this.Dispatcher; return (target == null || dispatcher == null || (target is DispatcherObject && (dispatcher.Thread.ThreadState & (ThreadState.Aborted | ThreadState.Stopped | ThreadState.StopRequested | ThreadState.AbortRequested)) != 0 )); } } public void Invoke(object arg, params object[] args) { object target = this.Target; Dispatcher dispatcher = this.Dispatcher; if (!this.IsDisposable) { if (this.IsDispatcherThreadAlive) { dispatcher.Invoke(DispatcherPriority.Send, new EventHandler( delegate(object sender, EventArgs e) { this.handlerInfo.Invoke(target, new object[] { arg, e }); }), arg, args); } else if (target is DispatcherObject) { dispatcher.BeginInvoke(DispatcherPriority.Send, new EventHandler( delegate(object sender, EventArgs e) { this.handlerInfo.Invoke(target, new object[] { arg, e }); }), arg, args); } else { ArrayList paramList = new ArrayList(); paramList.Add(arg); paramList.AddRange(args); this.handlerInfo.Invoke(target, paramList.ToArray()); } } } public bool DelegateEquals(Delegate other) { object target = this.Target; return (target != null && object.ReferenceEquals(target, other.Target) && this.handlerInfo.Name == other.Method.Name); } }
If you look closely you can see that DispatchHandler holds weak reference to target object therefore it will allow subscriber to be garbage collected. The important function here is DispatchHandler.Invoke(object arg, params object[] args). This function try three ways to invoke call on target object, first it invoke synchronous call through dispatcher if thread is Alive and not in sleep mode, otherwise it checks if target object is a DispatcherObject class object which means it required same thread (UI thread in which it is created) to fire handler, then fire handler asynchronously but on DispatcherObject‘ s thread and in the last it fires handler in current thread which causes event to occurred synchronously. IsDisposable check many scenarios to verify that handler is still active (one it verify that target object is still alive and not yet been collected by GC). This finally solved over problem of holding strong reference to subscriber object and allow it to be garbage collected since it has its weak reference. DispatchEvent is simple class that holds DispatchHandler objects and fires them, here is implementation:
public class DispatchEvent { private List<DispatchHandler> handlerList = new List<DispatchHandler>(); public void Add(Delegate handler) { this.Add(handler, Dispatcher.CurrentDispatcher); } public void Remove(Delegate handler) { var rmvHandlers = (from dispatchHandler in handlerList where dispatchHandler.DelegateEquals(handler) select dispatchHandler).ToArray(); if (rmvHandlers != null && rmvHandlers.Length > 0) { this.handlerList.Remove(rmvHandlers[0]); rmvHandlers[0].Dispose(); } } public void Fire(object sender, EventArgs args) { var disposableHandler = from handler in handlerList where handler.IsDisposable select handler; foreach (DispatchHandler rmvHandler in disposableHandler.ToArray()) { this.handlerList.Remove(rmvHandler); rmvHandler.Dispose(); } foreach (DispatchHandler handler in handlerList) handler.Invoke(sender, args); }}
Current thread Dispatcher can also be get from Dispatcher.CurrentDispatcher. It has simple Add, Remove and Fire to invoke all handles in FCFS way. Fire function also checks for disposable handlers. it you can see that handler reside in memory until event is occurred, well it is not as harm full as heavy .NET UI object memory leaked.
Implementing Dispatched Event Collection:
I have implement derived class DispatchedObservableCollection<Titem> derived from ObservableCollection<T> and override same functions as discussed “Solution to Problem” section. DispatchedObservableCollection<Titem> implementation:
public class DispatchedObservableCollection<Titem> : ObservableCollection<Titem> { DispatchEvent collectionChanged = new DispatchEvent(); DispatchEvent propertyChanged = new DispatchEvent(); protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { //base.OnCollectionChanged(e); this.collectionChanged.Fire(this, e); } protected override void OnPropertyChanged(PropertyChangedEventArgs e) { //base.OnPropertyChanged(e); this.propertyChanged.Fire(this, e); } public override event NotifyCollectionChangedEventHandler CollectionChanged { add { this.collectionChanged.Add(value); } remove { this.collectionChanged.Remove(value); } } protected override event PropertyChangedEventHandler PropertyChanged { add { this.propertyChanged.Add(value); } remove { this.propertyChanged.Remove(value); } } }
Implementation Test:
This is simple test application with a fork button that create same window in a different thread and also subscribe a DummSubscriber object to this photos collection (Photos collection is defined in application resources). Photos Class derived from DispatchedObservableCollection<Titem>, It also has internal file watchers that are tracking image folder for any new picture and then add it to photos collection, this intern trigger CollectionChanged event (same other thread). As soon as you close forked window (second window) DummSubscriber and forked window is collected by GC. Click here to download visual studio solution.
Comments on: "WPF Data Binding ObservableCollection (cross thread binding support)" (2)
[...] Post has been moved too another blog >> http://csharplive.wordpress.com/2008/09/11/wpf-data-binding-observablecollection-cross-thread-bindin… [...]
What if there is a serious drawback: the BeginInvoke? Asynchronous update means there is no guarantee that fast sequence of updates on work thread will keep its order on UI thread, is it a threat? I feel there could be a mess. Maybe there is no difference for UI would be if some items deleted at first and then some other items added to the collection, and not vice versa, but may be the difference exists.
And the other definitely a threat is that one about what would be if UI thread is slow and work thread that generates notifications is very fast – at some time there would be a numerous unhandled asynchronous notifications. What can you say about these things?