torsdag 14. juli 2011

WPF, Notifications and Parallellism

I'm working on getting our GUI responsive, which can be quite a challenge. We fetch a pdf or tiff over a wcf-service from the test-server running on a vm, rendering the first page and showing this to the user. We also have a strict policy on separating as much as possible into the domain-layer, and the domain-layer can not have any gui-classes. Fetching and rendering is done in the domain-layer, and the GUI is notified through the INotifiyPropertyChanged-mechanisms.
I've had several problems with the ownership of wpf-objects when a task in the domain-level sets a property which the gui subscribes to, and the gui then tries to redraw some metainformation in an overlay canvas. I've solved several situations by using TPL's ContinueWith-mechanism where the new task is run on the GUI-thread.
After working on a problem today, where images are fetched every time the user changes the selected row in a table, I started thinking if there is a better way to do this. If I could only get all notifications to run on the GUI-thread, without having to use TPL's ContinueWith...
So I've now created a small test-project that has some classes, a window showing info, and some extension methods that run all the notifications on the GUI-thread.
This is the essence of the extension methods:
public static TaskScheduler GuiContext;

public static void Notify(this PropertyChangedEventHandler self, INotifyPropertyChanged sender, string propertyName) {
if (self == null) return;
Task.Factory.StartNew(() => self(sender, new PropertyChangedEventArgs(propertyName)), CancellationToken.None, TaskCreationOptions.None, GuiContext);
}
The GuiContext field needs to be set on startup from the MainWindow, e.g. in the Loaded-event. It appears as though the extension-methods works like a charm.
I made the test-app start several threads that updates the objects in the controller several times, and as long as I allow the gui some cpu-time(put in some small sleeps in the threads), the gui is responsive and it updates the textboxes with values from the different threads.
I'm aware that just updating the same values from several threads is not a good idea, but for showing data in the gui I'm really just interested in showing the latest values, and I have control over cancellelation on the background tasks in the real application. So for the purpose of the test-app I don't think this is important.

I'm writing this post in the hopes that someone will take a poke at my project and tell me if they find any flaws or large weaknesses that I've overlooked. Here is a link to the project file.