Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
3.92/5 (5 votes)
14 Feb 2021CPOL4 min read 8.2K   300   5   3
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:

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

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

C#
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:

C#
/// <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:

C#
// 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:

C#
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):

C#
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):

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
Norway Norway
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionFeedback Pin
PureNsanity2-Jun-21 17:46
professionalPureNsanity2-Jun-21 17:46 
AnswerRe: Feedback Pin
Kenneth Haugland2-Jun-21 21:36
mvaKenneth Haugland2-Jun-21 21:36 
For this Tip, I mainly read Sascha Barbers articles like VM Behavior etc and thought I had to write a lot of code to achieve VM first. This was mainly a reminder to me on how I did this part, although I'm not really sure it's the best way.

To be honest, I have just used Prism to split up the Views with, for now. So I normally create a folder for View, ViewModel and Model in my Prism Module.

As for the IoC, I do have some questions on what to put into this container, how you solve for instance a shared settings file in the application. I created a single instance (singleton) with the settings in the IoC container. In the window where I could change the settings, I used reflection to set the same variables in the SettingsViewModel. For different attempts, I thought this always got messy...
GeneralRe: Feedback Pin
PureNsanity3-Jun-21 18:49
professionalPureNsanity3-Jun-21 18:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.