Background
In the previous post I had demonstrated how to use MVVM pattern in Silverlight 4 application. There were couple of places where I mentioned that it wasn’t purely MVVM style of coding and I’ll revisit those aspects later. Today I’ll refactor one such part which was related to showing of the MessageBox on click of the button.
Problem with using UI elements in View Model
One of the biggest advantage of using MVVM or any other separation pattern or presentation pattern as they are commonly called is the ability to test the functionality irrespective of user interface. If we use MessageBox in our ViewModel we can’t test it using unit tests. It becomes difficult to simulate for diifferent user actions like clicking an Ok button or a Cancel button in a message dialog.
ViewModels are just like any other classes and hence they support code reuse. This is really helpful when we want to share the same view model between lets say a WPF application, a Silverlight application and a Windows Phone 7 application. If we intend to do so then because of the capabilities of each of these frameworks the MessageBox might behave differently. Or it might not offer the same set of functionalities. To avoid such situations we can abstract the MessageBox functionality into a independent DialogResult service which can then be implemented as per the framework capabilities. This dialog service can be injected into the view model and the same view model can be reused across multiple technologies. For the time being lets concentrate on removing the dependency in a Silverlight application’s view model. I’ll reuse most of the code from the previous post.
How to remove dependency on Framework elements using service injection
In order to remove the tight coupling between the view model and the user interface element which is MessageBox class, we can decouple them by using the commonly followed approach of dependency injection. We’ll abstract the MessageBox class into a service and inject it into the view model. Lets start with the first step of abstracting the MessageBox class into a service interface.
public interface IDialogService
{ void Show(string message); }
The Show method that we had used earlier has been abstracted into a service called IDialogService. If we are using other methods of the Messagebox class we can declare them here in this interface and provide concrete implementations of the same wherever required.
The next step is to inject this service / interface into the view model. We’ll modify the constructor of the MainWindowViewModel class to accept a dependency as shown below
public MainPageViewModel(IDialogService dialogService) { SearchCommand = new DelegateCommand(SearchNet, CanSearchNet); _dialogService = dialogService; }
Once this is done we can modify the method which used to call the MessageBox class to use this injected dependency.
private void SearchNet(object obj) { //MessageBox.Show("Searching for " + SearchText);
_dialogService.Show("Searching for " + SearchText); }
Once this is done we have successfully decoupled the view model from MessageBox class. Only thing which remains is the implementation of the IDialogService and ensuring that the concrete instance is passed to view model’s constructor. We’ll implement the IDialogService as
public void Show(string message) { MessageBox.Show(message); }
Pretty simple. We just moved the call from view model to a concrete class which implements the interface. And finally we’ll inject this DialogService instance into the view model. We can use any dependency injection framework of our choice like Spring.Net, Unity, CastleWindsor, Ninject, StructureMap etc. But for keeping this demo small and simple and not to introduce any external dependency on any of these DI frameworks I’ll use the poor man’s dependency injection technique and inject the dependency from the default constructor
public MainPageViewModel() : this(new DialogService()){}
Thats all. We can run the application and see that it works as before. Using this approach also helps in refactoring. We can start off with basic approach and as and when the team becomes comfortable with tools and practices we can refactor the code without impacting the functionality.
Conclusion
As one of the evangelist of MVVM said, if we wish to utilize the true value of MVVM, we should be able to test the application without opening the UI screens. For that reason our view models should not have any dependency on UI elements like dialogboxes. There are tools available to simulate UI gestures, but they again tend to make the coupling between frameworks and tests which makes it harder to decouple things. One example where many people are divided in opinion is the use of Visibility property on the view model. Some argue that we can use Visibility on view model. Others tend to favour value convertors to convert boolean to visibility.
I prefer to keep the view model technology agnostic. And hence would prefer to have a boolean in the view model which is bound to the visibility on the view using a value convertor. We’ll look into it later in another post. I hope this post was helpful in separating UI element from view model.
As always I have uploaded the complete working solution to dropbox. Until next time Happy Programming
No comments:
Post a Comment