Pages

Wednesday, 20 May 2015

A Humble Review of Josh Smith's MVVM Article

Foreword:


WPF Programmers who are familiar with MVVM have most probably read one of the first comprehensive articles ever published, written by Josh Smith  WPF Apps With The Model-View-ViewModel Design Pattern ".
This article is rightfully considered one of the best MVVM articles by many and even seen by some as the "premier guideline" article for the MVVM pattern.
Here, in this blog post I will try to analyze the code (can be downloaded from the same Microsoft Magazine at this download page). I will try to make a somewhat detailed review, providing bunch of diagrams assisting my review, in order to make some of the points more clear. I believe diagrams are sometimes a better way to describe a structure as visual guides like diagrams are claimed to be more cognitive and helpful than text.
I will attempt to cover the design in four parts (in 4 blog postings) and will try a top to bottom approach. When necessary, I will make references to other resources or blog posts in order to assist the description. I hope this article would contribute to the programming community and make MVVM design principles more clear.   


Introduction: 

I would strongly recommend the reader first to read the original article written by Josh Smith or at least the first parts of it , before continuing here. Also it is recommended to have the source code in front of you (download page). This is essential to understand what is written below and in the follow-up sections. Here. I will try to avoid repeating Josh's article as much as I can and do not intend to cut/paste excerpts from it (except a few pictures, may be). My intention is not to repeat what Josh has already written, but to complement the original article in my own ways. I think only this way, I can make a contribution, if any. I would like to apologize for all the mistakes (both grammatical and technical) upfront. Hope that this article will add some more value into the original one. I will strongly encourage constructive feedback from readers, which would help me to improve the article for the benefit of all.

Part#1:




The class diagram above shows the structure of the ViewModel design. There are five ViewModels used. We will describe in the final part (4th part) of this article exactly why five ViewModels are used, in other words the logic behind selecting ViewModels. For the time being, we shall take it for granted and carry on with the description. 


The MVVM uses multiple ViewModels, which control their own dedicated View space. The Command structure borrows the principle from Prism, using a separate Command class (class derived from ICommand) called RelayCommand, which is a simplified version of the DelegateCommand of Prism. The MVVM system can be separated into logical operational sections. The first section can be called "Setup" section, which handles all the operations which take place when the program is launched.

Before starting with the "Setup" section, we shall look briefly how the XAML script is laid down.

XAML Structure:


There are 2 general XAML scripts used. First one is called "MainWindow.xaml" and the other is called "MainWindowResources.xaml". The latter serves as a Resource for the MainWindow.xaml. Besides the main XAML script files, there are also UserContol XAML files. One is called "AlllCusomersView.xaml" and the other is called "CustomerView.xaml".




Command Structure:



ICommand methods:

ViewModel Classes
ICommand
CustomerViewModel
Public ICommand SaveCommand
WorkSpaceViewModel
Public ICommand CloseCommand
CommandViewModel
Public ICommand Command

SetUp Section:


When the application is launched, the App.xaml.cs will be executed first automatically by WPF. When the DataContext (i.e.Window.DataContext=viewmodel;) line is hit, the XAML Parser will fire up and start reading the XAML script and first will assign 2 items into the Control Panel HeaderedContentControl via the code at MainWindowViewModel (Note:when a collection is bound to an ItemsControl or ContentControl, all the members of the collection will be listed (rendered) on the itemscontrol (i.e.listcontrol) automatically by the XAML Parser).

//This method is called from the XAML file and executed by XAML parser (when DataContext line is hit at runtime)
       
public ReadOnlyCollection<CommandViewModelCommands
       {
           
get
           {
               
if (_commands == null)
               {
                   
List<CommandViewModel> cmds = this.CreateCommands();
                   _commands = 
new ReadOnlyCollection<CommandViewModel(cmds);
                  //This syntax creates a ReadOnly collection wrapper around the List cmds
               }
               
return _commands;
           }
       }
 
       
List<CommandViewModelCreateCommands()
       {
           
return new List<CommandViewModel>
//This is like List<t>{1,2,5,2,7}; here t=CommandViewodel and before adding we create the object
           {
               
new CommandViewModel(
                   
Strings.MainWindowViewModel_Command_ViewAllCustomers,
                   
new RelayCommand(param => this.ShowAllCustomers())),
 
               
new CommandViewModel(
                   
Strings.MainWindowViewModel_Command_CreateNewCustomer,
                   
new RelayCommand(param => this.CreateNewCustomer()))
           };
       }


  1. Create a new list to contain CommandViewModel objects and add 2 newly created CommandViewModel objects. The Constructor of CommandViewModel creates a 
    new RelayCommand class.
  2. For each command used within the MVVM design, a unique CommandViewModel class is created. 
    Also a unique RelayCommand class is generated (which is linked to the uniquely created CommandViewModel)
  3. When a RelayCommand class is generated, via it's Constructor, the methods "ShowAllCustomers" 
    and "CreateNewCustomer" is passed as delegates from the MainWindowViewModel to the particular RelayCommand.
  4. The purpose of using RelayCommand class is to avoid bunch of Command classes 
    (classes derived from ICommand) within the MVVM design.A different instance of same 
    RelayCommand will handle any command issued from within the MVVM system.
  5. The 2 newly created CommandsViewModel class objects are placed into a list and then wrapped by a Collection in order to be able to assign them to the itemscontrol object's itemsource property.



          Note that "ResXFileCodeGenerator" is used to create strongly typed strings.



After the startup routine is executed, the XAML Parser will know that there are 2 objects (CommandViewModel) in the itemscontrol as element and the XAML Parser uses the additional information (in MainWindowResources XAML script) to render the datatemplate. Here is the section which is responsible for rendering the view under the Control Panel HeaderedContentControl.

 

 
<!--
  This template explains how to render the list of commands on the left
  side in the main window (the 'Control Panel' area).
  -->
  
<DataTemplate x:Key="CommandsTemplate">
    
<ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="6,2">
      
<ItemsControl.ItemTemplate>
        
<DataTemplate>
          
<TextBlock Margin="2,6">
            
<Hyperlink Command="{Binding Path=Command}">
              
<TextBlock Text="{Binding Path=DisplayName}" />
            
</Hyperlink>
          
</TextBlock>
        
</DataTemplate>
      
</ItemsControl.ItemTemplate>
    
</ItemsControl>
  
</DataTemplate>


 

Clicking on the hypertext object on the ControlPanel:


Now the XAML Parser knows lots of things regarding the 2 items that has been rendered into the Control Panel. Those items are represented as hypertext on the panel, so they have a command property. When any of them is clicked, XAML Parser will do certain things in the background.  It will go right to the instance of CommandViewModel object, which is related to the hypertext, then will take the right instance of the RelayCommand object instance which is related to this particular CommandViewModel and then run it. Running of the RelayCommand Execute method will call the delegate which points to one of the methods listed at MainWindowViewModel (either  CreateNewCustomer() or ShowAllCustomers(), depending on which hypertext is clicked). Also note that at RelayCommand we need to use "parameter" (i.e. _execute(parameter)) in order to use the correct delegate). The diagram below tries to provide a visual description.


So, how does XAML Parser know where the command (ICommand property) resides? Well, first of all there is a definition at XAML (<Hyperlink Command="{Binding Path=Command}">). However wasn't the DataContext defined as MainWindowViewModel? Yes, but XAML Parser has intelligence. The binding path to Commands (Content="{Binding Path=Commands}"), at which the XAML Parser run the C# code from MainWindowViewModel, made XAML Parser aware of the instances of newly created CommandViewModel and RelayCommand class objects.  So, when the runtime compiler runs, it will not generate an exception, even though the DataContext is not pointing to the class with ICommand Property directly. Below the diagram tries to describe this routing logic.


 If we summarize the action for the command flow for this MVVM Model, we end up with the following diagram.




This command flow offers multiple advantages such as loose coupling (through delegates and ICommand) and modular design for unified command routing (using one RelayCommand class for all commands). There is a collection of CommandViewModel objects for different commands which use the same Command Class (ICommand derived Class) RelayCommand and all the Command executions are handled at the same class namely MainWindowViewModel.


 

This concludes the first part. In the second part of this series, I will focus more on setup and command structure.

No comments:

Post a Comment