Presentation Model with DataTemplates in CompositeWPF (Prism) Sample
September 8th, 2008
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).
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.
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).
Composite Application Guidance (Prism) vNext envisioning
September 4th, 2008
If you want to know what has been keeping us busy for Prism vNext, read this detailed post from David Hill.
An excerpt from his post:
Prism 2.0 will focus on two things:
- Extending the guidance delivered in Prism 1.0 for building composite WPF applications to also support composite Silverlight applications.
- Adding guidance for building ‘multi-headed’ applications - applications that can deliver both a desktop and an in-browser experience.
David Hill has been working and contributing with p&p in many projects, and has recently become the new architect at p&p. So be sure to add David to your RSS, because there’s probably lots more to come related to the guidance that p&p is providing not only with Prism vNext, but with other assets like EntLib.