I finally got some slack time to put a small sample of Presentation Models with DataTemplates on WPF together. As I mentioned in a previous post, the combination between Presentation Model and DataTemplates can become a differentiated quality feature in your source code.
Let’s explore the sample (download link on the bottom).

image

The application is based on the Commanding QuickStart we shipped in the first release of the Composite Application Guidance for WPF (a.k.a. Prism or CompositeWPF). The original QS was written using presentation models, but without using automatic DataTemplates, so I updated it to create this sample.

I did not change the basic structure of the solution or the code so much, so you can compare it with the one shipped with Prism. The post assumes familiarity with the Composite Application Library (CAL) included in CompositeWPF, so I won’t get into much detail on things like the Bootstrapper, Regions, Modules, IoC Containers or Commands.

image

Notice how the solution does not contain any UserControl for the views. The only Window here is Shell (in the Commanding project), but it is basically empty, except for some properties like size or background.

So if the Shell window is empty. How does the application expose the main regions for the modules to start adding views? Well, in the Commanding Bootstrapper of course.

1 class CommandingBootstrapper : UnityBootstrapper

2 {

3   protected override DependencyObject CreateShell()

4   {

5      Shell shell = Container.Resolve<Shell>();

6      shell.Content = Container.Resolve<ShellPresentationModel>();

7      shell.Show();

8      return shell;

9   }

10  //…

11 }

So there in line 6 we inject the ShellPresentationModel into the Shell window, and we have our first DataTemplate view injected automatically in the Visual Tree (remember that the presentation model is not a control, and is lookless). The view template is declared in ShellResourceDictionary.xaml and included in App.xaml.

1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

2 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

3 xmlns:local="clr-namespace:Commanding">

4   <DataTemplate DataType="{x:Type local:ShellPresentationModel}">

5    <Grid>

6    <Grid.RowDefinitions> … </Grid.RowDefinitions>

11     <ItemsControl ItemsSource="{Binding GlobalCommandsRegion.Views}" />

13     <ItemsControl ItemsSource="{Binding MainRegion.Views}" />

18   </Grid>

19  </DataTemplate>

20 </ResourceDictionary>

I stripped most of the styling parts. Notice that there is no RegionManager.RegionName attached property here. Instead there is a binding in ItemsSource to some MainRegion.Views property path in the ShellPresentationModel (the DataContext of the template is the instance of the DataType).

If we look into this ViewModel, you can see the regions being registered in the Region Manager and exposed to the View.

1 public class ShellPresentationModel

2 {

3   public ShellPresentationModel(IRegionManager regionManager)

4   {

5     MainRegion = new Region();

6     GlobalCommandsRegion = new Region();

7     regionManager.Regions.Add("MainRegion", MainRegion);

8     regionManager.Regions.Add("GlobalCommandsRegion", GlobalCommandsRegion);

9    }

10

11  public IRegion MainRegion { get; private set; }

12  public IRegion GlobalCommandsRegion { get; private set; }

13}

This keeps the composition at the Presentation Model level, instead of the UI being responsible of registering the regions.

One drawback of this particular implementation is that if I have a more complex Control that not only binds, but interacts with a region, there is no out-of-the-box behavior that will use Region Adapters to enhance this binding (in the example the ItemsControl just binds to the Views collection exposed by the region and that’s it).

But don’t discard this approach prematurely; if you need such a behavior, it could be easily solved by creating an attached behavior other than RegionManager.RegionName (BTW, this attached property IS an attached behavior…) that takes an existing region in the model and hooks it up with the target Control appropriately.

Now that the shell piece is ready to be used by CAL modules, let’s look at the OrderModule class.

1 public class OrderModule : IModule

2 {

3-11

12  public void Initialize()

13  {

14    RegisterResources();

15

16    IRegion mainRegion = regionManager.Regions["MainRegion"];

17    mainRegion.Add(container.Resolve<OrdersEditorPresentationModel>());

18

19    IRegion globalCommandsRegion = regionManager.Regions["GlobalCommandsRegion"];

20    globalCommandsRegion.Add(container.Resolve<OrdersToolbarPresentationModel>());

21   }

22

23   private static void RegisterResources()

24   {

25     ResourceDictionary dictionary = new ResourceDictionary();

26     dictionary.Source = new Uri("pack://application:,,,/Commanding.Modules.Order;Component/OrdersResourceDictionary.xaml");

27     Application.Current.Resources.MergedDictionaries.Add(dictionary);

28   }

29 }

As you can see, the first thing the Initialize method does is register the resources, by merging the Application’s resources with the OrdersResourceDictionary.xaml file. This allows having the resource dictionary files in the modules they belong. If you want to include the resources in the App.xaml file, you need to place the referenced files in the main project, making the idea of modularity senseless. More on this in my previous post.

Then the Initialize method adds the presentation models (as opposed to the Views in the original QuickStart) to the regions in order for WPF to "show" them, by automatically injecting the correct template defined in the resource dictionary.

If you dig into the OrdersResourceDictionary.xaml file, you’ll find some DataTemplates for the presentation models, and also some styles, converters and templates used to show validation errors in the the view. These are there to show that not only trivial views can benefit of this pattern, but also views that has some UI logic that is not a simple binding of a textbox.

And if converters are not enough for your templates, don’t hesitate on adding attached behaviors to cover the lack of code-behind gap, and still keep testability and reusability to the maximum.

My opinion on converters: just because they exist and can be very helpful, try to keep these in low numbers, and, instead, expose properties in the presentation models that can be easily bound to. Even if you can test these converters separately from the views, they can start to make your code less intuitive. Nevertheless, try to keep a balance and create a presentation model that is at the same time agnostic of the view that will represent it, but easily bound from a view (which is the purpose of a presentation model).

As you develop the views, you’ll notice the importance of commands to execute logic in the presentation models, as you cannot handle the Click event in the code-behind. In this example we’re not using the RoutedCommands that come with WPF, but another implementation (DelegateCommand) that is included in CompositeWPF. You can also use attached behaviors that end up invoking commands or actions on the presentation model in response to other control events.

Download the code to get into the implementation details for the views.

Disclaimer

This code is provided "AS IS" with no warranties. You can use it freely. It includes the binaries of Composite Application Library and Unity Application Block.

Download

Download the Presentation Model with Prism Sample (for Visual Studio 2008).

kick it on DotNetKicks.com

  • Ahmad

    I love your idea and I have a problem with the TabControl, how I can activate a new added view while the baControl.ItemsSource is bound to the views and there is nothing that tell the tab control how to activate a new added view !

    I have a solution and I don’t sure is it is the best way to do this:

    I Create an ActiveDocument property in the Shell presentation model(that alrady expose a MainRegion as a property) and i monitor the changing in the MainRegion.ActiveViews so I can change the ActiveDocument at the right time, finaly I Set the SelectedItem property of the TabControl in the XAML resource file to this property.

    I you have a better option please let’s me know.
    good work.

  • jdominguez

    @Ahmad
    That sounds good.
    Another approach could be to create an attached behavior that receives an instance of an IRegion. Whenever you set a region on that property, you could apply a Region Adapter that implements the functionality you’re talking about. You could even use the SelectorRegionAdapter from Prism, with some minor tweaks to be able to accept an existing region.
    This way you could bind your TabControl with something like the following:

    Please let me know how this goes.

  • Ahmad

    Thinks to you, but I think that an example will be very helpful.

    And also , when you say an attached behavior, you mean an attached property ?

  • jdominguez

    @Ahmad
    Sorry for the late response. When I say attached behaviors, I mean this pattern: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
    This is implemented by using attached properties, but the concept goes a little beyond that, because it adds a behavior instead of just setting a value.
    In this case, when a region is assigned to that attached property, you could take the control it’s being assigned to, and use a (modified) SelectorRegionAdapter to hook up the TabControl and the passed region correctly.
    I’ll try to pull out a sample when I get some slack time.

  • http://wpfclient.blogspot.com/ gromas

    Hmmm… excelent!

    But “cal:” namespace works fine with templates…

    … and now we have an empty ShellPresentationModel:

    class ShellPresentationModel{}

  • http://wpfclient.blogspot.com/ gromas

    <ItemsControl cal:RegionManager.RegionName=”{x:Static inf:RegionNames.GlobalCommandsRegion}”/>