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