My ultimate goals are to develop code that is consistent, easy to read and follow, and hopefully automatically testable via unit testing or other automation.
The first thing I propose doing is removing control logic from the ViewModel. In most MVVM implementations, you see the VM containing the control logic, for example, if a "Save" button is clicked, we see the VM responding to the command and writing the data to the database. I don't like this. It's not that having it in the VM is a bad thing, it is encapsulated and away from the view. But to me the VM should be light weight, responsible only for transforming model data into a data format easily digestible by the view and for gathering user input back from the view.
By moving control logic, validation, etc. into a controller class, this allows the view model to focus on it's job, makes it easier to read, and I think makes it easier to test the controller.
Here is a diagram of my structure. Unlike most MVVM models, I take into account using MVVM on custom controls as well as Main UI windows. Designing a custom control (that derives from User Control) and using the MVVM pattern requires departure from the traditional MVVM pattern.
In looking at the model, we see a departure from the classic MVVM model where the view only accesses only the ViewModel. If the setup is for a top-level window, and not a control, then things become just like the classic MVVM model.
But when making a user control, their are things that belong to the view in the WPF world and we really need them to notify the controller. Dependency properties are a prime example of this. They are attached to the code-behind of an XAML file, which really belongs to the view. In this case, the view needs to inform the controller module that a dependency property has been updated and let the controller respond appropriately. And the controller needs to inform the view when a dependency properties value should change. If we are not working on a user control, then everything goes back to normal MVVM where the view has no knowledge of the controller or view model.
However, if we look at a dependency diagram, I think the view have knowledge of the controller is OK. After all, dependencies still only go one way and this is fine. It still allows us to replace the view with a unit test and not have anything break.
Messaging up the chain takes place via events. If the ViewModel needs to notify the View of changes, this is accomplished by raising an event and allowing the View to listen for it. The WPF way of doing this is to use the INotifyPropertyChanged interface.
It is critical to use interfaces whenever possible. An interface allows us to swap out components with testable components. An interface between the ViewModel / Controller and Model allows us to unit test the ViewModel / Controller by creating a mock model.
In developing the ViewModel and Controller, it is critical that these classes be separated from any UI components, including opening new windows. Every action within the view model and the controller should be completely contained. Opening a new window would be instantiating another view, which violates the requirement that the ViewModel and Controller have no knowledge of the View. Additionally moving things on and off the UI thread is something that belongs in the domain of the View. However, often it is the Controller that makes this move.
In order to accomplish application tasks, such as opening new windows and putting things onto the UI thread, interfaces should be created to concrete classes responsible for operating in the View space. This configuration allows these objects to be mocked out during unit testing. This will be the subject of a future blog post.
I hope to put some example code up soon as well.
Very good explanation... Thanks
ReplyDelete