Andy's observations as he continues to attempt to know all that is .NET...

Saturday, October 21, 2006

SynchronizationContext assists layering

Having just got back from two weeks travelling I was confronted with the need that my oldest daughter needs to learn her times tables for a tester after half term. Being away a lot and the children having loads of after school clubs etc means we often find it hard to find the right moment to practice. So I decided to write an app to help test them on it…Ok so I’m sure there are tons of them out there…but you can’t beat a home grown solution ;-)

Seriously there is a couple of areas I am focusing on at the moment,

  • Ramping up for DM’s .NET architecture course.
  • WPF.

This seemed like a good exercise where I could put both these skills to good use. Now even DM instructors have time pressures this app needed to be written like yesterday if the kids are to start using it to get ready for their test. My WPF skills aren’t there yet that I could confidently build the UI that quick, but in a few weeks I do want to add a WPF gui. So this creates a great architectural opportunity for me to structure the solution into clear layers…Model, View and Controller. The View and Controller layer is specific to WinForms and I obviously want to write as little code here as possible, putting most of my focus on the Model, in this case a Times Table Test engine.

First I built the model defining a type to represent the test, and exposing properties for three possible answers and a running score. The type also had methods for requesting the next question and submitting an answer. In order to make the view be updated by changes in the model I had to implement the INotifyPropertyChanged interface on each model type in order for them to notify the UI of any changes to the model.

Then I moved onto building a form to display the current question. Labels were used to pose the question, and to display the score etc. The possible answers were represented by buttons. Having laid out the form it was then just a question of wiring up, the various labels and buttons to display the correct values, this was achieved through the use databinding via bindingSources, all the wiring up done inside the designer. I then had to wire up the actions for the various buttons to communicate the users response to the question and to move the question forward. But So far so good…virtually no UI code written.

However whilst this was ok to make the app really useful the user needs to enter the response in a finite period of time. So this created two options implement a timer in the UI layer or in the model…For me I obviously wanted to place it into the model since that piece of code would be reused when I implement the WPF UI.

So off I went and added a Timer thread to my model, the activity on this timer thread would cause various bound properties in the model to be updated, which then had the effect of firing PropertyChanged events to all objects bound to these properties. Since some of these bindings where to UI components this would result in the UI component being updated on a thread other than the thread used to create the UI component. Attempting to update a UI component on a foreign thread is technically not allowed, and in .NET 2.0 if you have a debugger attached and you attempt to do so you get a cross thread exception…to my surprise I didn’t get one…just the update’s failed to be propagated to the UI. This I thought was a bit odd…but since the update didn’t happen I guess it’s by design Im not 100% sure of the exact reasoning behind this..Anyway for me I need to fix this situation since I did want my UI to be updated.

In order to solve this problem I would need to cause the event to actually be delivered on the UI thread. The conventional way to do this in WinForms is to use a pair of methods on every control called InvokeRequired and BeginInvoke. But this would mean that my model code would need knowledge of winforms to ensure that all events fired on the UI thread Yuk and breaks my architectural model.

There is however a more general way of doing this using something called SynchronizationContext, this is an abstract class that provides a way of marshalling a delegate onto a specific thread. You can ask WinForms for the current SynchronizationContext and it will return an object that can be used to post work back onto the UI thread. You write your code against the abstract class, and assuming other UI implementations that require thread affinity also support this then my mode can be written just the once. In fact WPF does have support for this so cool…the model can be written to deliver PropertyChanged events on the correct thread. To ensure my property events are marshalled onto the appropriate thread I have a method which looks as follows.

private
void SafePropertyChangeNotification(string property)

{


if (syncCtx != null)

{

syncCtx.Post(delegate

{

OnPropertyChanged(new
PropertyChangedEventArgs(property));

} , null);

}


else

{

OnPropertyChanged(new
PropertyChangedEventArgs(property));

}

}

What I’m struggling to understand is why they MS never added support in the BindingSource type to perform the marshalling operation, they are after all detecting the attempt to perform a cross thread operation and silently ignoring it, giving you the option to configure the binding source to marshal the update would have been dead cool.

Anyway the SynchronizationContext does allow me to decouple my model from my UI implementation, and does allow me to write a single piece of marshalling code that will work with WPF or WinForms..

The first version of the times table tester is now complete, the UI layer is extremely thin…and I even managed to deploy it in our home network using ClickOnce…I know scary I actually taught the kids how to just say ok to the not trusted publisher prompt….

Over the next few weeks I’m going to build a WPF version of the UI and assuming no hidden gotcha’s Im just simply going to replace the UI layer with a new one


 

You can download the full code from my download page


 


 


 


 


 

No comments:

About Me

My photo
Im a freelance consultant for .NET based technology. My last real job, was at Cisco System were I was a lead architect for Cisco's identity solutions. I arrived at Cisco via aquisition and prior to that worked in small startups. The startup culture is what appeals to me, and thats why I finally left Cisco after seven years.....I now filll my time through a combination of consultancy and teaching for Developmentor...and working on insane startups that nobody with an ounce of sense would look twice at...