In the app that I am working on right now, there is a particular design pattern that I am using to communicate back and forth with the server side data. The app itself is structured into three distinct pieces:
The first time I designed the communication channel, there were events on the VM that mirrored the operations that were present on the SM. These VM-events were fired on the UI thread using Dispatcher.BeginInvoke. The UI subscribed to all of these events and did some visual changes. This model worked great for the initial set of operations that we had in the SM. As the number of operations grew (currently there are about 8 different ones), I realized that the event model is not scaling well.
Instead of the VM raising an event on the UI thread, I decided to use the power of delegates to automatically invoke them on the UI thread whenever an operation completed. Let me explain this more clearly.
As mentioned earlier, the VM already has a reference to the Dispatcher. Note that when I say the VM has reference to the Dispatcher, it is really the root of the ViewModel, a singleton, that stores the reference to the Dispatcher. Instead of mirrored Service Model events, the VM now exposes methods with an Action delegate as a parameter. Let me show you the most commonly used method, EnqueueTask:
public void EnqueueTask(ServerTaskType task, Action completedUIAction)
{
if (task == ServerTaskType.Initialize)
{
EventHandler<FeedDownloadedEventArgs> handler = null;
handler = (sender, args) =>
{
_controller.HistoryDownloaded -= handler;
OnHistoryAvailable(args, completedUIAction);
};
_controller.HistoryDownloaded += handler;
_controller.Initialize();
}
else if (task == ServerTaskType.Reload)
{
EventHandler<FeedDownloadedEventArgs> handler = null;
handler = (sender, args) =>
{
_controller.HistoryDownloaded -= handler;
OnHistoryAvailable(args, completedUIAction);
};
_controller.HistoryDownloaded += handler;
_controller.ReloadHistory();
_controller.GetPeerList();
}
}
The above method does the actual subscription to the Service Model event and also unsubscribes when the operation completes. It then raises the completedUIAction delegate. This action is invoked using Dispatcher.BeginInvoke so it always happens on the UI thread. Here is the OnHistoryAvailable method, which does the job of updating the ViewModel and then calling the action to update the UI (using a custom InvokeOnDispatcherconvenience method). Note that we are passing the action delegate to this method.
private void OnHistoryAvailable(FeedDownloadedEventArgs e, Action completedAction)
{
InvokeOnDispatcher(
delegate
{
// Update the ViewModel
// <Code deleted for brevity>
// Finally call the completed action, which will update some UI
if (completedAction != null)
{
completedAction();
}
});
}
Good question! As you can see, I no longer need to expose any further events from the VM, which the UI subscribes to. The event handler code on the UI is now passed in as an Action delegate which the VM nicely calls on the UI thread. Also this technique is scalable to a large number of Service Model events. All I change on my side is the switch case for the operations.
[Currently I am using this technique to show/hide progress bars and other animations. The animation starts before a call to EnqueueTaskand stopped in the completedUIAction.]
If we have a large number of SM-events, we can have a simple mapping table that maps the operation-type to a method on the VM. We can then simply lookup that method from the table and pass it the completedUIActiondelegate. This can save us a long switch-case in EnqueueTask.
We can also use generic Action<T>
delegates that could be used to pass
some state information back to the UI.
Hopefully you will find this technique useful in your own apps !