Prism Walkthrough Part 4: Use Delegate Commands

The final source code can be downloaded here!

In this post we’ll bind a Command to our Menu so that it’s able to add views to the MainRegion.

Create a MenuViewModel:

 

 

Insert following code into the MenuViewModel:

 
   1:  public class MenuViewModel
   2:  {
   3:      private readonly IRegionManager _regionManager;
   4:      private readonly IUnityContainer _container;
   5:   
   6:      public MenuViewModel(IRegionManager regionManager, IUnityContainer container)
   7:      {
   8:          this._regionManager = regionManager;
   9:          this._container = container;
  10:      }
  11:  }

 

Here we created our first ViewModel that will be the target of our View binding.   Our View reference our ViewModel but the ViewModel does not need a reference to a view. The view binds to properties on a ViewModel, which, in turn, exposes data contained in model objects and other state specific to the view. The bindings between view and ViewModel are simple to construct because a ViewModel object is set as the DataContext of a view. If property values in the ViewModel change, those new values automatically propagate to the view via data binding. 

 

When implementing MVVM you need to bind the View and ViewModel, therefore you have the choice between several options.  You can declare the ViewModel as a resource in your view xaml, you can also use  a presenter class that is responsible to instantiate and bind the View to the ViewModel.  This strategy is useful when you need to bind different Views to the same ViewModel.  Because we don’t need this flexibility here we chooses for a simpler design; here it’s the view itself that instantiate and bind to his ViewModel.  Some could claim that I’m not implementing the pure MVVM pattern as in this application the View knows his ViewModel.  I nevertheless chooses for this approach because it’s the simplest way to implement MVVM and also because it enable me to handle UI events like KeyboardPress events gracefully  (see folowing post). 

 

In this design, the ViewModel, never the View, performs all modifications made to the model data. The ViewModel and model are unaware of the View but the View reference his ViewModel.  This design still provide loose coupling in the sence that the ViewModel does not know about the View but it makes some concessions to the purity of MVVM.  Nevertheless these concessions pays dividends in many ways, as you will see in the next series.

 

To setup this binding update the MenuView class:

 

   1:  public partial class MenuView : UserControl
   2:  {
   3:      public MenuView(IRegionManager regionManager,IUnityContainer container)
   4:      {
   5:          InitializeComponent();
   6:          this.DataContext = new MenuViewModel(regionManager, container);
   7:      }
   8:   
   9:      public MenuViewModel ViewModel
  10:      {
  11:          get
  12:          {
  13:              return this.DataContext as MenuViewModel;
  14:          }
  15:      }
  16:  }

 

When the user clicks a MenuItem in the MenuView, a command on the MenuViewModel should execute to display a view in the MainRegion (see line 36 - MenuViewModel).   Here we use the Prism DelegateCommand to implement the Command pattern. 

 

To retrieve the right View we choose for convention over configuration.  The  LoadViewCommand takes a parameter that is the the name of the View without the suffix View. 

The ViewModel has exactly the same name as the View but ends with ViewModel ->see MenuViewModel line 39.

To implement this convention we need to register the Views with a name parameter via the Unity container in the Module Initialization –> see Coremodule line 8 & 9.

 

Add the following code to the CoreModule.cs:

 

   1:  public void Initialize()
   2:  {
   3:      this.RegisterViewsWithRegions();
   4:  }
   5:   
   6:  protected virtual void RegisterViewsWithRegions()
   7:  {
   8:      this.regionManager.RegisterViewWithRegion(RegionNames.MenuRegion, typeof(MenuView));
   9:      this.regionManager.RegisterViewWithRegion(RegionNames.StatusbarRegion, typeof(StatusbarView));
  10:  }

 


Update the MenuViewModel:

 

   1:  public class MenuViewModel : INotifyPropertyChanged
   2:  {
   3:      private readonly IRegionManager _regionManager;
   4:      private readonly IUnityContainer _container;
   5:          
   6:      public MenuViewModel(IRegionManager regionManager,IUnityContainer container)
   7:      {
   8:          this._regionManager = regionManager;
   9:          this._container = container;
  10:          this.LoadViewCommand = new DelegateCommand<object>(this.LoadView, this.CanLoad);
  11:              
  12:      }
  13:   
  14:      #region PropertyChanged
  15:      public event PropertyChangedEventHandler PropertyChanged;
  16:          
  17:      private void OnPropertyChanged(string propertyName)
  18:      {
  19:          PropertyChangedEventHandler handler = this.PropertyChanged;
  20:          if (handler != null)
  21:          {
  22:              handler(this, new PropertyChangedEventArgs(propertyName));
  23:          }
  24:      }
  25:      #endregion
  26:   
  27:      #region LoadViewCommand
  28:      public DelegateCommand<object> LoadViewCommand { get; private set; }
  29:      public event EventHandler<DataEventArgs<string>> LoadedView;
  30:   
  31:      private bool CanLoad(object arg)
  32:      {
  33:          return true;
  34:      }
  35:          
  36:      private void LoadView(object obj)
  37:      {
  38:          IRegion mainRegion = this._regionManager.Regions[RegionNames.MainRegion];
  39:          string viewName = obj.ToString() + "View";
  40:          var view = this._container.Resolve<IView>(viewName);
  41:          mainRegion.Add(view,viewName);
  42:          mainRegion.Activate(view);
  43:          this.OnLoadView(new DataEventArgs<string>(obj.ToString()));
  44:      }
  45:   
  46:      private void OnLoadView(DataEventArgs<string> e)
  47:      {
  48:          EventHandler<DataEventArgs<string>> loadHandler = this.LoadedView;
  49:          if (loadHandler != null)
  50:          {
  51:              loadHandler(this, e);
  52:          }
  53:      }
  54:      #endregion
  55:  }

 

Bind the LoadViewCommand in our MenuItem.xaml:

 

   1:  <Menu Name="menu1">
   2:      <MenuItem Header="File">
   3:          <MenuItem Header="New"  CommandParameter="CreateFile" Command="{Binding Path=LoadViewCommand}"/>
   4:          <MenuItem Header="Folder"  CommandParameter="CreateFolder" Command="{Binding Path=LoadViewCommand}" />
   5:      </MenuItem>
   6:  </Menu>

 

 

 

You should be able to run the application and when you click the menu File, you should see :

 

 

 

This end the Walkthrough series.  In these 4 parts we setup the foundation of our client application implementing the MVVM pattern and learned how to use Prism to create a modular architecture.  

 

In the following posts I’ll provide a complete application build on the architecture described in part 0 -> (WPF/WCF/EntityFramework).   The application extends this basic design and provide an example on how to use the asynchronous behavior of web services to create responsive applications.

 

 

kick it on DotNetKicks.com

Prism Walkthrough - Part 3: Add Views to Regions

Source code of this part can be downloaded here.

In this part we’ll create our first views and display them inside their regions.  The views are the controls that display our content.  Because our application is build with modularity in mind it’s the Module and not the Shell that should define which views need to be added to the regions. 

 

 

1) We add a folder “Views” to our Module project and 4 views inside this new folder: 

  • CreateFileView:  One of the views that can be displayed in the MainRegion
  • CreatFolderView: Another view for the MainRegion
  • MenuView: Control that will be responsible to Add views to the MainRegion
  • StatusBar: Control that will be added to the StatusBarRegion

 

 

To add views to our regions we use the prism region manager service.  The region manager service is responsible for maintaining a collection of regions and creating new regions for controls.  Typically, we interact directly with region manager service to locate regions in a decoupled way through their name and add views to those regions. By default, the UnityBootstrapper base class registers an instance of this service in the application container. This means that we can obtain a reference to the region manager service in our application by using dependency injection.

We use constructor dependency injection to gather an instance of the RegionManager and store it in a local field and it’s  in the Initialize method of our CoreModule class that we implement logic to add the initial views to our Regions. 

 

2) Modify the CoreModule class like:

   1:  public class CoreModule : IModule
   2:  {
   3:      private readonly IRegionManager regionManager;
   4:   
   5:      public CoreModule(IRegionManager regionManager)
   6:      {
   7:          this.regionManager = regionManager;
   8:      }
   9:   
  10:      public void Initialize()
  11:      {
  12:          this.RegisterViewsWithRegions();
  13:      }
  14:   
  15:      protected virtual void RegisterViewsWithRegions()
  16:      {
  17:          this.regionManager.RegisterViewWithRegion(RegionNames.MenuRegion, typeof(MenuView));
  18:          this.regionManager.RegisterViewWithRegion(RegionNames.StatusbarRegion, typeof(StatusbarView));
  19:      }
  20:  }

 

 

To overcome magic constants in our code  we used a class containing all our region names.

 

3) Add this RegionNames class to your Infrastructure Library:

   1:  namespace PrismWalkthrough.Infrastructure
   2:  {
   3:      public class RegionNames
   4:      {
   5:          public const string MainRegion = "MainRegion";
   6:          public const string MenuRegion = "MenuRegion";
   7:          public const string StatusbarRegion = "StatusbarRegion";
   8:      }
   9:  }

 

 

Finally to see something displayed in our Menu and StatusBar we need to add some content inside these controls:

 

4) Modify the MenuView.xaml:

<StackPanel>
    <Menu Name="menu1" Background="Transparent" >
        <MenuItem Header="File" >
            <MenuItem Header="New"  CommandParameter="CreateFile" />
            <MenuItem Header="Folder" CommandParameter="CreateFolder"  />
        </MenuItem>
    </Menu>
</StackPanel>

 

5) Modify the StatusBarView.xaml:

<Grid>
    <TextBlock Text="This is the StatusBar" />
</Grid>

 

When pressing F5 you should see:

 

 

kick it on DotNetKicks.com

Prism Walkthrough - Part 2: Define The Regions

Define the Regions

 

Our shell will contain 3 regions:

  • Menu Region: contain a Menu that is responsible to load views in the Main Region
  • Main Region: region that will load the workspace controls – these are the views that will handle the application features. In our application only one view at a time can be loaded in the Main Region.
  • Status Bar Region: provide info about current application state 

 

These regions are the placeholders for the controls defined in our Modules.  Our application will contain only one Module: the CoreModule but this application can easily be extended by adding new modules.   In our application the regions are defined as ItemsControls  in the Shell.xaml file and can be accessed in a decoupled way by their names; they support dynamically adding or removing views at run time.

  1. To add the regions in the Shell window add the following namespace definition to the root Window element. You need this namespace to use an attached property for regions that is defined in the Composite Application Library:  xmlns:cal=http://www.codeplex.com/CompositeWPF.
  2. Replace the Grid in the Shell by the following xaml:
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="35" />
            <RowDefinition Height="*" />
            <RowDefinition Height="35" />
        </Grid.RowDefinitions>
        <ItemsControl Name="MenuRegion" cal:RegionManager.RegionName="MenuRegion" 
    VerticalAlignment="Top" Grid.RowSpan="2" Height="26" HorizontalAlignment="Left" Background="Transparent" Margin="0,12,0,0"/>
    <Grid Margin="4,4,4,4" Grid.Row="1"> <Border Background="GhostWhite" BorderBrush="LightGray"
    BorderThickness="1" CornerRadius="5" Margin="0,0,0,0"> <ItemsControl Name="MainRegion"
    cal:RegionManager.RegionName="MainRegion" Margin="4" Height="291" />
    </Border> </Grid> <Border Grid.Row="2" > <ItemsControl Name="StatusbarRegion"
    cal:RegionManager.RegionName="StatusbarRegion" Background="Transparent" />
    </Border> </Grid>

 

Now you should be able to run the application and see this screen:

 

 

Next --->

kick it on DotNetKicks.com

 

 

 

 

Prism walkthrough - Part 1: Create The Shell

 

The client side application is made out of three assemblies:

  • The Shell (PrismWalkthrough.Shell)
  • The Modules ( PrismWalkthrough.Modules)
  • The Infrastructure (PrismWalkthrough.Infrastructure)

The foundation of the application is “The Shell”.  The Shell is the top-level window of an application based on the Prism Composite Application Library. This window is a place to host different user interface (UI) components that exposes a way for itself to be dynamically populated by others, and it may also contain common UI elements, such as menus and toolbars. The Shell window sets the overall appearance of the application.

 

In our application it’s the shell that is responsible to load the Modules. A module represents a set of related concerns. It can include components such as views, business logic, and pieces of infrastructure, such as services for logging or authenticating users. Modules are independent of one another but can communicate with each other in a loosely coupled fashion.  In this walkthrough our application will contain only one module: the CoreModule nevertheless the modular architecture enable us to easily extend the application adding new modules later on.

 

The Infrastructure Assembly is a shared library referenced by both the shell project and the module projects, and holds shared types such as constants, event types, entity definitions and interfaces.

 

Create the Solution

 

  1. Create the Shell project (PrismWalkthrough.Shell) application with Visual Studio-> File, Project, WPF Application.
  2. Add the Modules project and Infrastructure project –> File, Add New Project, Classlibrary
  3. The three projects should reference the Composite Application Library assemblies, add the following reference:
    (These assemblies can be found in the october 2009 Guidance of Prism -  you will need to compile the sample application)
    1.  
      • Microsoft.Practices.Composite.dll
      • Microsoft.Practices.Composite.Presentation.dll
      • Microsoft.Practices.Composite.UnityExtensions.dll
      • Microsoft.Practices.Unity.dll
      • Microsoft.Practices.ServiceLocation.dll
  4. Add a reference to the Modules and Shell projects referencing the Infrastructure assembly.
  5. Add a refrence to the Shell project referencing the Modules assembly.
  6. Add a refrence to the Modules and infrastructure that reference to the WindowsBase & PresentationCore and PresentationFramework –> Reference, Add, .Net tab.

 

Your solution should look like this:

 

 

 

 

Initialize the application

To enable modularity and dependency injection in our application we need to make some changes to the standard WPF application we just created.  First we setup a bootstrapper.  The bootstrapper is responsible for the initialization of the application, this is realized by overriding the CreateShell method.

  1. Add a new class on the root of the application containing the shell (PrismWalkthrough.Shell), name it Bootstrapper
  2. The Bootstrapper should inherit from Microsoft.Practices.Composite.UnityExtensions.UnityBootstrapper class and override the CreateShell method. You need to instantiate the Shell through the Unity conatiner and call his Show method
   1:  public class Bootstrapper : UnityBootstrapper
   2:  {
   3:      protected override System.Windows.DependencyObject CreateShell()
   4:      {
   5:          Shell shell = this.Container.Resolve<Shell>();
   6:          shell.Show();
   7:          return shell;
   8:      }
   9:  }

 

To be able to run the bootstrapper at least one module should be registered in our application. 

  1. Add a class to the PrismWalkthrough.Module and name it CoreModule.cs
  2. This class should implement the IModule interface:
   1:  namespace PrismWalkthrough.Modules
   2:  {
   3:      public class CoreModule : IModule
   4:      {
   5:          public void Initialize()
   6:          {
   7:              
   8:          }
   9:      }
  10:  }

 

Through the bootstrapper you must configure which Modules are available in our Shell. You need to register at least one module in your bootstrapper.

  1. Override the InitializeModules in the bootstrapper:
   1:  protected override void  InitializeModules()
   2:  {
   3:      IModule coreModule = this.Container.Resolve<CoreModule>();
   4:      coreModule.Initialize();
   5:  }

 

The WPF Application class needs to execute the bootstrapper by calling his Run method. 

  1. First you should delete the StartupUri in the App.Xaml file.
    <Application x:Class="PrismWalkthrough.Shell.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    </Application>
  2. Override the OnStartup method in the App.cs file:
   1:  protected override void OnStartup(StartupEventArgs e)
   2:  {
   3:      base.OnStartup(e);
   4:      Bootstrapper bootstrapper = new Bootstrapper();
   5:      bootstrapper.Run();
   6:  }

 

Now you should be able to run you application, you should see a white screen.

Next --->