Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF with PRISM - View and ViewModel First Implementation with Unity IoC

0.00/5 (No votes)
14 Feb 2021 2  
Useful extension method for ViewModelFirst
This tip will show you how to set up both ViewModel first and View first implementation in PRISM with Unity IoC.

Introduction

PRISM is generally a very effective tool for dividing up a user interface into regions. This makes it possible to split up and divide the code into useful sections that are easily maintainable. In general, there are two ways of doing this Module separation: View first and ViewModel first. By far, the most extensive method used is View first.

But ViewModel first has its merits too, but it is cumbersome to set up, as you need to link up the style of the control in a ResourceDirectory, and couple that to the resource in the application. This article shows the implementation of both methods in the same PRISM module, with an extension method for finding XAML files that effectively draws the content into a region.

Also, I had designed an Enum to ComboBox binding that did not work properly in my attached property article. I decided to fix that and include it without too much explanation.

Background

The View first means that the View is creating the ViewModel, an approach that makes you effectively design a WPF user control that you link up. Obviously, this couples the ViewModel and the View. But it is easy to program and in most cases, it's just usually the simplest method for the lone programmer.

The ViewModel first is a bit more versatile. This means that it is simpler to reuse the "view" as a DataTemplate elsewhere. And also, if you are working with a team of designers, it is easier to just send them the DataTemplate. This way, you can work in parallel in a way that is not so easily done with View first.

In the early days of the WPF design face, they also discussed allowing you to connect Interfaces to a DataTemplate view. They decided against it as you could have multiple views for the same interface, which would make it difficult to assign the correct one. Using a variant of this is the third option you have. Assign the connection between the View and the ViewModel at design time. Like a ViewConnectionModel if you will. However, this requires you to use the user control interface in order to implement a connection method for the View and ViewModel.

What You Should Know

In order to set up a PRISM application, I'd advise you to use the templates that are created by the PRISM team. Actually in general, if you are really new to PRISM, I think it would be best if you watch some of Brian Lagunas's videos on the Outlook program he designed from scratch: Prism for WPF - Building Outlook. The series is really long, but it will go through all the parts of the application design. There are also some good articles here on CodeProject about implementing a PRISM pattern in your application which I'd also advise you to read.

In general, I view the PRISM tool as a way to separate the front end code, the code between the View and the ViewModel. For the service and interactions to other parts, let's say a web service or some other calculation service, PRISM does not offer a clear guideline on how to separate that part. Sure it offers some aggregated events that allow you to communicate via the IoC container and some cool tools for handling complex events. But in the model part of the program, you are pretty much on your own, so to speak.

The Code Part

This is pretty much really simple. I add the ResourcesDirectory of the main entry point of the application to the container:

containerRegistry.RegisterInstance<ResourceDictionary>(Current.Resources);

Now I can access the resources from the Module and simply apply the local ResourceDirectories to the assembly:

public MainExampleModule(IRegionManager regionManager, 
        IContainerProvider containerProvider, IEventMessenger eventMessager)
{
    // Get main resource directory
    containerProvider.Resolve<ResourceDictionary>()
    // Add via an extension method all local ResourceDirectories 
    // (XAML files with Views Style etc) placed in any 
    // folder in "this" project called "Views"
    .MergeResourceDirectories(this, "Views");

The extension method is rather simple although not really a bulletproof production code yet:

/// <summary>
/// The function adds all xaml files in the folder found in the current project.
/// </summary>
/// <param name="main">Main resourcedirectory. 
/// Has to be initialized as an instance in the Bootstrapper</param>
/// <param name="current">The link to the current project with the 
/// "this" parameter passed in</param>
/// <param name="folder">The folder as for instance "/Views" where 
/// all the resource files are located</param>
public static void MergeResourceDirectories
       (this ResourceDictionary main, object current, string folder)
{
    string[] files = GetXAMLResourceDirectories
                     (current, GetXAMLResources(current, folder));
                     
    if (files != null)
        {
        // Loop through all ResoureDirectories that should be merged with 
        // its main resource directory
        foreach (string item in files)
        {
            // Assembly path to resource
            string name = current.GetType().Assembly.GetName().Name + 
                          ";component/" + item;
                          
            ResourceDictionary resource = new ResourceDictionary
            {
                Source = new Uri(name, UriKind.RelativeOrAbsolute)
            };
            // Add the resource if main ResourceDirectory does not already have it
            if (!main.MergedDictionaries.Contains(resource))
                main.MergedDictionaries.Add(resource);
        }
    }
}

The only thing that makes this somewhat complicated is that UseControl has a XAML file but also a XAML.cs file. So I don't want to add the XAML which has a cs file with the same name. Otherwise, just add the resources to the main application if it's not added.

This actually allows me to write this working code:

// Register View and ViewModel to the same region
// Could call containerProvider.Resolve<ViewBViewModel>() also
RegionManager.Regions["ContentRegion"].Add(new ViewBViewModel() 
{ Message="ViewB - ViewModel First"});

This incidentally is not the best practice but gives you an idea about how it applies the view in the region. As you will see also, it can add multiple different views into the same region. They can be interchanged by calling activate:

RegionManager.Regions["ContentRegion"].Activate(...);

For View first, it is rather simple. Register the view in the region (here it also registers ViewB via an extension method):

public void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterViewModelForNavigation<ViewBViewModel>();
            RegionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
}

The navigation between the views is simply done in an Rx callback (the code are identically between View and ViewModel first):

void ViewAMessageCallback(ViewAMessage message)
{
   var parameters = new NavigationParameters
   {
       { "count", RegionManager.Regions[message.Region].Views.Count() }
       };
    var uri = new Uri(typeof(ViewA).FullName, UriKind.RelativeOrAbsolute);
    RegionManager.RequestNavigate(message.Region, uri, parameters);
}

The navigation parameters are also sent and could be retrieved in each of the respective ViewModels.

That's All Folks

So I hope this will help you somewhat in the PRISM world.

History

  • 14th February, 2021: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here