Pages

Wednesday, 20 May 2015

A Humble Review of Josh Smith's MVVM Article Part#2



In Part2 of the MVVMDemo application, we will focus on the 2nd function of the MVVM design, namely how the "WorkSpaces" are handled. This function is handled very similar to the first function "Commands" within the MVVM system.  If we shall remember the startup routine, the XAML Parser was running the "Commands" method from the MainWindowViewModel (our DataContext), the code was creating a Collection (readonly), adding 2 objects (CommandViewModel), creating a RelayCommand (ICommand derived class) for each object added. Then XAML Parser would assign the collection to the ItemsControl's ItemSource Property and rendering the DataTemplate according to the XAML script. The ultimate goal of all this initial setup was to 1-Create the command structure to handle the commands coming from our UI (i.e.Click on the hypertext on Control Panel) in a modular and loosely coupled way and 2-XAML  Parser reading all the details of this (command) structure (Note: XAML Parser has build in intelligence to record objects involved in the setup run and retrieve them during activation run).

 

The "WorkSpaces" initial setup routine works in a similar fashion. As opposed to the "Commands" initial setup however, the function (goal#1) is to create a mechanism for "closing" the workspaces, which were launched and handle the disposal process (garbage collection) and RequestClose delegate stack in an orderly fashion, again in a modular and loosely coupled way. Goal#2 remains always the same. Now lets dig deeper into the concept.



If we pay attention to the code at "Workspaces" method (MainWindowViewModel), the return type is an "ObservableCollection", which really plays a very important role in a MVVM design. It is a collection which has build-in notification functionality, should objects added, removed and modified. This automated functionality and the fact that WPF framework objects (like ItemsControl) have plug-in capabilities to this collection, making it an ideal tool for an object container in WPF and especially in MVVM design.

/// <summary>
        
/// Returns the collection of available workspaces to display.
        
/// A 'workspace' is a ViewModel that can request to be closed.
        
/// </summary>
        
/// 
 
            
public ObservableCollection<WorkspaceViewModel> Workspaces
        {
            
get
            {
                
if (_workspaces == null)
                {
                    _workspaces = 
new ObservableCollection<WorkspaceViewModel>();
                    _workspaces.CollectionChanged += 
this.OnWorkspacesChanged;
                }
                
return _workspaces;
            }
        }

    void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)
       {
           
if (e.NewItems != null && e.NewItems.Count != 0)
               
foreach (WorkspaceViewModel workspace in e.NewItems)
                   workspace.RequestClose += 
this.OnWorkspaceRequestClose;
 
           
if (e.OldItems != null && e.OldItems.Count != 0)
               
foreach (WorkspaceViewModel workspace in e.OldItems)
                   workspace.RequestClose -= 
this.OnWorkspaceRequestClose;
       }
 
       
void OnWorkspaceRequestClose(object sender, EventArgs e)
       {
           
WorkspaceViewModel workspace = sender as WorkspaceViewModel;
           workspace.Dispose();
           
this.Workspaces.Remove(workspace);
       }

In the first function "Commands", the object created and added into the collection (ReadOnlyCollection) was "CommandViewModel", in the second function "Workspaces", the object created and added into the collection (ObservableCollection) is the "WorkSpaceViewModel". Let's try to make the structure clearer by a block diagram (which would show the parallel similarity in between those function flows). 




As we can see the sequence is almost identical for the commands. Store classes exposing ICommand property into a WPF collection (ObservableCollection, ReadOnlyCollection), then set the path to Command Class (Class inherited by ICommand-in this case RelayCommand), then delegate to methods which are located in the same VIEWMODEL Class (here MainWindowViewModel), where the command is first received. Here is a simplified diagram which summarizes this description.  

 
In an MVVM structure, the two most significant aspects are modular design  and loose coupling (i.e for Unit Testing, maintainability,expandibility). Here we can see that all of those requirements are met. To start with, we have a loose coupling thanks to WPF build-in functionality of binding concept and we further use ICommand and delegate concept to make the design even more loosely coupled. We can see that with this decoupled design above, all the different classes can be Unit Tested easily, without affecting the other.

The summary can be laid down as follows.

·         Use a ViewModel (#1) to confront commands coming from the UI,

·         Expose the ICommand Property via a separate ViewModel (#2) (here CommandViewModel or WorkSpaceViewModel),

·         Use a single and separate Command class (ICommand derived class) for handling all the commands in the MVVM system (here RelayCommand) (Also note:Prism uses a "DelegateCommand" class, which is basically the same as RelayCommand)

·         And finally execute the commands in the first ViewModel through event invocation.



At Setup process#2 (workspaces), a new ObservableCollection (_workspaces) is created to hold the WorkSpaceViewModel objects (could be either CustomerViewModel or ALLCustomersViewModel).

When a new workspace is added (by clicking to one of the hypertext rendered images on the Customer Panel), a new WorkSpaceViewModel object instance is created and added into the ObservableCollection WorkSpaces. This will trigger the event handler OnWorkspacesChanged, which in turn will add a new delegate (method pointer) linked to the justly added WorkSpaceModel object's RequestClose Event (This Event is handled at MainWindowViewModel as well).

(workspace.RequestClose += this.OnWorkspaceRequestClose;)



Now,  when an already created workspace is clicked close (Close button on top of the window),  the ObservableCollection (WorkSpaces) will trigger the event handler OnWorkspacesChanged again, which in turn will remove the old delegate (method pointer) linked to the current WorkSpaceModel Object. 

(workspace.RequestClose -= this.OnWorkspaceRequestClose;)

This will trigger "OnWorkspaceRequestClose" handler method, which would first dispose the WorkSpaceModel object (Garbage collection) and then remove it from the Workspaces collection (visually removing it from the UI).



Some WPF (Non MVVM) Details:

If we look closer to MainWindowViewModel class, we see a method called "SetActiveWorkspace". This function is called from within "CreateNewCustomer" and "ShowAllCustomers" methods (which launch workspaces). The purpose of this function is to have a focus on the newly created WorkSpace (created by clicking on either hypertext). What is going on the background? In order to understand what WPF does in the background, we need to have some idea about what the "CollectionViewSource" is. This class has been added with .NET 3 framework and for the sole purpose of manipulating (adding,sorting and grouping) collections directly from XAML. So why do we use it in the code? Well, CollectionViewSource can be used in C# code like many other XAML usable classes, but the good thing is that it has a property called "DefaultView" which returns the CollectionView of a collection (refer to Binding to Collections and Collection Views). Once we create an instance for the CollectionView for the WorkSpaces collection, we can manipulate the collection from the code as well. (note that CollectionView class is created by the WPF automatically whenever certain collections (i.e. ObservableCollection) are created). Here by code, we are moving CollectionView pointer to current (newly created) workspace (collectionView.MoveCurrentTo(workspace);).


The next article of this series will study the addition (and saving) of a new customer to the customer base. 

No comments:

Post a Comment