A while ago I wrote a post called “Silverlight Child Window With MEF and MVVM Light” where I laid out an approach on how to implement a child window in Silverlight while following MVVM. Some of the challenges of using MVVM center around keeping UI specifics out of the view model. Keeping UI out of the view model is not mandatory but seems to a generally accepted practice. So how do you launch UI without referencing UI? In my previous approach I used MVVMLight’s to message to the active view to launch the dialog. The dialog would then message back with any return results that the view model would handle. A big component for passing objects around was MEF. In this post I am going show a couple of different ways to accomplish the same thing without using MEF, though I am still using MVVMLight.
The ultimate goal is to keep the UI specifics out of the view model. This separation increases the reusability of the view models while at the same time allowing for maintainability and testability. Another goal is to abstract out the launching of child windows so that they can be easily launched from any view. A good example of this is an Error Window. I will show an example of this in this post.
In this post I will show launching a dialog that requires passing data from the view. I will show an example that returns data from a dialog that the view model can consume. Finally I will show an example that takes my ReturnAction<T> object (described in my last post here) to handle passing data back including any exceptions. You should be able to handle any situations by interchanging these building blocks.
All of my child windows have their own view models so I am limiting the amount of code I place in my view’s code behind. I am not an MVVM purist. I believe you can put code in the code behind as long as you limit it to view specific code and do not do any business logic there. In my case I will be setting properties on the DataContext (ViewModels). I will also be closing the child windows from the code behind as you will see in later examples.
The first step is to abstract the launching of the dialogs. To accomplish this I create a navigation service. Here is the interface for my navigation service:
As you can see I have three different child windows in this demo, each requiring different data. Note you could also add a method for navigating to other pages if you are using a Silverlight Navigation application. This would allow you to navigate from page to page from the view models.
The actual implementation is responsible for launching the child windows passing any required arguments. Here is what my implementation looks like:
Note the two different ways I am passing arguments to the child windows. In the first method I am passing an exception to the constructor of the ErrorView child window. I will then pass this parameter to the DataContext in the code behind of the ErrorView. The second dialog takes no arguments and returns a message that gets displayed in the main view. The final dialog takes a ReturnAction<T> which it uses to pass the results back to the main view.
The NavigationService is injected into the ViewModels from the ServiceProviderBase through the ViewModel Locator (described in my last post here). I treat the NavigationService just like I treat the my service wrappers from my service layer. The NavigationService provides a service for the view model.
A Reusable Error Dialog
One of the most common dialog in an application should be the error dialog. There should be a mechanism to display friendly messages when an exception happens. This is especially useful when fault exceptions come back from service calls. I will be showing an example of this when I show the dialog that works with my ReturnAction<T>. For now I am going to explain how I implemented the ErrorView dialog.
You can see from the code above that I am passing the exception to the DisplayError method in my navigation service. It then calls the constructor on the ErrorView passing the exception:
In the constructor I am casting the DataContext as an ErrorViewModel. I the set a property, that I created, with that exception. The DataContext is set in my Xaml (described in my last post here). Through the magic of data binding the dialog calls ToString() on the exception and displays it to the user. If the dialog looks familiar it is because it is the same one that gets created by the “Silverlight Business Application” template in the New Project dialog. This shows how easily that can be adapted to fit this architecture.
Now to launch the dialog I bind a command to the button from the MainViewModel:
That is how easy it is one line of code (line 77).
Handling Return Data
The next example will show how I handle dialogs that need to return data to the underlying View/ViewModel. In this example I am going to use the Messaging capabilities of the MVVMLight framework. There is nothing fancy with how I create the dialog. It is exactly the same way I created the ErrorView dialog above except that I am not passing any arguments. This is the dialog that gets launched:
I bind a command to the ‘Ok’ button from the ViewModel:
Here the Messenger class takes a MyMessage object and sends it out setting TheMessage property with the text in the TextBox. In the MainViewModel I subscribe to the message from the dialog. It takes the MyMessage object and uses it to set a property on the MainViewModel that is bound to a TextBlock on the view:
The final thing to do is close the dialog. Since we removed the click handler from the ‘Ok’ button, the code that usually closes the dialog no longer gets called. So I subscribe to the same message in the dialog’s code behind. In the Action of the message I call the code that closes the dialog.
One thing to note, it does not matter what you set DialogResult to. We are not subscribing to the close event of the dialog. We are handling any business logic (whether the user clicked ‘Cancel’ or ‘Ok’) through messaging. It is up to the view models to take appropriate actions. Theoretically you could hold off sending the message until a service returned and not do it right on the button click. This would allow for a save action, for instance, to complete prior to closing the dialog. But the decision to close now resides with the view models and not the view.
An Alternate Way
The previous example is one way to get data back from a dialog. Here is another way. It is a combination of the ErrorView (getting an object in) and the last example getting data back. In the ErrorView example I use the view code behind to set a property on the DataContext. Here is an alternate way to accomplish the same task without using the code behind:
To understand what the ReturnAction<T> is look at my previous post here. In a nutshell it captures a return object or and exception and allows the caller to create a call back method that gets called when the Notify method is called. Here is the ReturnAction<T> getting created:
The constructor takes an Action callback which I will show in a few minutes. In the dialog I create commands to bind to buttons on the dialog. For this example I simulate a message and simulate an exception being thrown:
(Note: I still have to use the Messenger to close the dialog).
In the MainViewModel I can now handle the action appropriately. If there is a message I can display it and if there is an error I can handle it.
These example should give you some ideas as to how easy it is to use MVVM in your Silverlight Applications while dealing with Child Windows. With a little bit of infrastructure in place you are able to work with different views passing data back and forth.
If you want to play with the code you can get it here.