.NET & Funky Fresh

Syndication

News

  • <script type="text/javascript" src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&amp;MarketPlace=US&amp;ID=V20070822/US/bluspiconinc-20/8001/8b68bf4b-6724-40e7-99a5-a6decf6d8648"> </script>
Dispelling a Common WPF/SL Myth

Everywhere I look I find WPF/Silverlight developers who believe a very popular myth:  You cannot update a ViewModel or an ObservableCollection from a non-UI thread.  Like most myths, there is an element of truth here.  But not understanding that truth can lead you to some very elaborate solutions.  Here’s the truth: You cannot fire the change notification from a non-UI thread.  So with that in mind, it becomes very easy to abstract thread synchronization away.  Here’s how we do it in Caliburn:

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public virtual void NotifyOfPropertyChange(string propertyName)
    {
        Execute.OnUIThread(() => RaisePropertyChangedEventImmediately(propertyName));
    }

    public virtual void RaisePropertyChangedEventImmediately(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class BindableCollection<T> : ObservableCollection<T>, IObservableCollection<T>
{
    public BindableCollection() {}

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        Execute.OnUIThread(() => RaisePropertyChangedEventImmediately(e));
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        Execute.OnUIThread(() => RaiseCollectionChangedEventImmediately(e));
    }

    public void RaiseCollectionChangedEventImmediately(NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
    }

    public void RaisePropertyChangedEventImmediately(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
    }
}

The Execute.OnUIThread is a helper that Caliburn provides to abstract away the underlying IDispatcher.  When Caliburn’s dispatcher is asked to execute something on the UI thread it first checks the thread it is on.  If it is already on the UI thread, it simply invokes the delegate normally.  Otherwise it marshals the call to the UI thread.  This approach also makes testing easy, because you will never find any threading code in a ViewModel.  Everything ultimately goes through an IDispatcher which Caliburn provides a fake implementation of for testing purposes.


Posted 09-08-2009 12:13 PM by Rob Eisenberg
Filed under: , , , ,

[Advertisement]

Comments

Ayende Rahien wrote re: Dispelling a Common WPF/SL Myth
on 09-08-2009 2:57 PM

That is not quite accurate, actually.

The common issue is the notifications on the right thread, yes, but you also have issues with the UI thread getting notifications about a collection change while another thread is modifying the collection. You can get into nearly impossible to figure out errors at that stage.

Rob Eisenberg wrote re: Dispelling a Common WPF/SL Myth
on 09-08-2009 3:06 PM

True.  I tend to think of that as a separate issue, though.  It's probably not the common case.  At least, I've never seen it outside of NHProf.

DotNetShoutout wrote Dispelling a Common WPF/SL Myth - Rob Eisenberg - Devlicio.us
on 09-09-2009 6:23 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

MM wrote re: Dispelling a Common WPF/SL Myth
on 09-16-2009 11:49 AM

I am not sure it is true.

I have a sample program where I invoke PropertyChanged from a different thread and View is able to update without any.

Not sure what WPF is doing under the hood, when we set the datacontext wpf must  be checking if it is INotifyPropertyChanged and subcribing to PropertyChanged event of the datacontext.

and in the callback must be doin gui thread switch.

is it true ...

MM wrote re: Dispelling a Common WPF/SL Myth
on 09-16-2009 1:56 PM

Actually it depends upon the element

For e.g. in my Test example I bind TextBlock.Text to my ViewModel and TextBlock works fine event if NotifyPropertyChanged is fired from different thread.

Where as ListView is not the same

lilikindsli wrote re: Dispelling a Common WPF/SL Myth
on 09-30-2009 4:31 AM

QyLvZ9 I want to say - thank you for this!

lilikindsli wrote re: Dispelling a Common WPF/SL Myth
on 10-01-2009 1:09 AM

72clqq I want to say - thank you for this!

Andrew Smith wrote re: Dispelling a Common WPF/SL Myth
on 10-01-2009 8:28 AM

This is untrue. You're making the assumption that something is not  iterating/manipulating the collection on the UI thread. Let me give you an example, you could have a DataGrid on the UI thread that is bound to the collection. It could be in the middle of iterating/enumerating the collection and a context switch occurs. The background thread runs and clears the list. It doesn't matter that the notification will be invoked on the UI thread. When the context switches back to the UI thread and the grid continues enumerating the collection it will crash. A good explanation of the problem can be found here: bea.stollnitz.com/blog

Rob Eisenberg wrote re: Dispelling a Common WPF/SL Myth
on 10-01-2009 10:17 AM

@Andrew

Good feedback.  However, if you read all of Bea's post you will note that she says " This has nothing to do with Avalon, it’s just a plain multithreading problem."  That is, you only have the problem you are speaking of when you are doing a more general threading no no.  Or when you are doing a CLR no no, such as modifying a collection while it is being enumerated.  You still have to manage your threads appropriately if you are doing more complex threading in your application and you have to be sure not to break fundamental .NET "laws."  My solution isn't for that.

Andrew Smith wrote re: Dispelling a Common WPF/SL Myth
on 10-01-2009 10:43 AM

Then I'm unclear as to what this "solution" is intended to help. It would seem that this is only helpful when you are in complete control of all usage of the collection. If you have controls bound to that collection (e.g. a DataGrid, ItemsControls, etc.) then you are not in control and cannot use this approach without still encountering race conditions. So wouldn't the actual truth be "You cannot make changes to a collection to which you have one or more elements bound.". Your article would mislead someone into thinking that simply raising the change notification on the UI thread makes their collection thread safe. All that does is avoid the direct exception which is meant to warn you that you are violating thread safety and instead you are opening yourself up to hard to track/intermittent race conditions.

Andrew Smith wrote re: Dispelling a Common WPF/SL Myth
on 10-01-2009 11:13 AM

I should clarify. Perhaps the solution of marshalling the notification for a property change of an item is ok. I'm commenting on doing this for collection change notifications as you have done in your snippet.

Globals wrote re: Dispelling a Common WPF/SL Myth
on 10-03-2009 3:37 AM

all good things

Romase wrote re: Dispelling a Common WPF/SL Myth
on 10-04-2009 1:53 AM

site best

Gykoth wrote re: Dispelling a Common WPF/SL Myth
on 10-10-2009 10:27 PM

Great site. Keep doing.,

Egor wrote re: Dispelling a Common WPF/SL Myth
on 11-10-2009 6:41 PM

Common myths stay that way because there is so much vague information around.

Yes you can raise the property changed event on another thread and bound controls will update without any problems - the WPF property observer does its own marshalling.

Yes you will run into problems if you try to change a bound collection in another thread.

I generally deal with this by creating a dispatcher observable collection class, which marshalls add/remove etc operations to the UI thread. I also made sure that it supports bulk adds/removes.

This guy (can't find his name on his own blog)

blog.quantumbitdesigns.com/.../collections

has another solution for updating collections from multiple threads, as well as a good explanation of the problem. Also, the issue may be fixed in 4.0

NHibernate blog wrote NHibernate and WPF: Asynchronous calls
on 11-22-2009 8:52 PM

I will show in this article an approach to make an asynchronous call to the model, to prevent the user

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)