Prism for WPF: A Basic Primer (2024)

This article will provide a basic platform from which you can delve deeper into other aspects of Prism.

Introduction

Prism is a framework that enables development of loosely coupled applications that are flexible, maintainable and easy to test. Prism applications are made up of modules – loosely coupled functional units that encapsulate portions of an application's overall functionality. In a team setting, modules can be individually developed, tested and deployed, thus minimizing cross-team dependencies and enabling teams, or individuals in a team, to focus on specific aspects of an application. Prism can be used to develop either desktop or mobile applications, that follow the MVVM design pattern, as it supports creation of WPF, Xamarin and UnoPlatform projects.

Background

This article provides a basic overview of Prism for WPF using a sample application that displays profiles of imaginary staff members. The sample project can be cloned or downloaded from GitHub.

Prism for WPF: A Basic Primer (1)

The sample application.

The sample application contains four projects: a WPF application project; a class library with shared code; and two Prism modules.

Prism for WPF: A Basic Primer (2)

TIP: The easiest way to set up a Prism project is by first installing the VS Prism Template Pack extension and making use of the Prism project templates. The main project in the sample application was created using the Prism Blank App (WPF) template while the modules were added using the Prism Module (WPF) template.

Prism for WPF: A Basic Primer (3)

Some Prism project templates.

Prism

Shell, Regions & Views

Prism applications are made up of a shell which hosts all the visual components of an application. As is the norm in Prism, and by default, the Prism app project template sets MainWindow as the application's shell.

C#

public partial class App{ protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } ...}

A shell contains one or more regions where modules can inject views, and a view can in turn contain regions where other views can be placed. The following diagram, from the Prism documentation, highlights this setup,

Prism for WPF: A Basic Primer (4)

To place a view into a region, modules make use of the RegionManager which keeps track of all the regions in an application. The sample application has only one region named ContentRegion,

XML

<mah:MetroWindow x:Class="StaffStuff.Views.MainWindow" ... xmlns:prism="http://prismlibrary.com/" xmlns:common="clr-namespace:StaffStuff.Common;assembly=StaffStuff.Common" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" ... prism:ViewModelLocator.AutoWireViewModel="True" ...> ... <Grid> <ContentControl prism:RegionManager.RegionName= "{x:Static common:RegionNames.ContentRegion}" /> </Grid></mah:MetroWindow>

The sample application has two views: A user control that displays cards containing some employee details and another user control that displays more details of a specific employee. The latter user control also contains a button for navigating back to the first view.

Prism for WPF: A Basic Primer (5)

The two views are in a module named UIModule and as you can probably tell, this module is UI specific. (The modules in the sample application are organized in 'horizontal layers'. If the application was organized around vertical slices each of the views would be in different modules and those modules would contain functionality specific to a view.)

UIModule implements Prism's IModule interface. This interface has two methods; one that is called when the module is initialized and another that's used for type registration (I set Unity as the IoC container when creating the project. The Prism project wizard offers you the option of using either Unity or DryIoc).

C#

using StaffStuff.UI.Views;using Prism.Ioc;using Prism.Modularity;using Prism.Regions;using StaffStuff.Common;namespace StaffStuff.UI{ public class UIModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<IRegionManager>(); regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(StaffView)); } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<StaffView>(); containerRegistry.RegisterForNavigation<StaffDetailsView>(); } }}

In OnInitialized(), I'm requesting the RegionManager to place StaffView in ContentRegion. As a result, StaffView will be the default view that is displayed when the application is launched. In RegisterTypes(), I'm specifying that both views should be available for navigation. This enables moving back and forth between the two views using Prism's navigation capabilities.

View Models & Navigation

Each of the views has its data context set to a view model that is wired to the view using Prism's ViewModelLocator. Prism associates a view model with a view using a default convention which assumes that a view model is in the same assembly as the view; is in a .ViewModels child namespace; has a name that corresponds with that of a view; and that the corresponding view is in a .Views child namespace. (If you prefer using a different convention, you can specify a custom one.) The UI module has two view models, each associated with a specific view.

Prism for WPF: A Basic Primer (6)

The view models inherit a base class that inherits Prism's BindableBase and implements its INavigationAware interface.

C#

using Prism.Mvvm;using Prism.Regions;namespace StaffStuff.UI.ViewModels{ public class ViewModelBase : BindableBase, INavigationAware { protected IRegionManager RegionManager { get; } public ViewModelBase(IRegionManager regionManager) { RegionManager = regionManager; } public virtual bool IsNavigationTarget(NavigationContext navigationContext) { return true; } public virtual void OnNavigatedFrom(NavigationContext navigationContext) { } public virtual void OnNavigatedTo(NavigationContext navigationContext) { } }}

BindableBase is simply Prism's implementation of INotifyPropertyChanged while INavigationAware enables a view model to participate in the navigation process.

StaffViewModel contains a DelegateCommand whose execute method instructs the RegionManager to navigate to the staff details view – DelegateCommand is Prism's implementation of the ICommand interface.

C#

using System.Collections.Generic;using Prism.Commands;using Prism.Regions;using StaffStuff.Common;using StaffStuff.Common.Interfaces;using StaffStuff.Common.Models;namespace StaffStuff.UI.ViewModels{ public class StaffViewModel : ViewModelBase { private List<Employee> _employees; public List<Employee> Employees { get => _employees; set => SetProperty(ref _employees, value); } public DelegateCommand<Employee> EmployeeDetailsCommand { get; } public StaffViewModel(IStaffData staffData, IRegionManager regionManager) : base(regionManager) { Employees = staffData.GetEmployees(); EmployeeDetailsCommand = new DelegateCommand<Employee>(StaffDetails); } private void StaffDetails(Employee employee) { var parameters = new NavigationParameters(); parameters.Add(nameof(Employee), employee); RegionManager.RequestNavigate (RegionNames.ContentRegion, "StaffDetailsView", parameters); } }}

A parameter is passed in the navigation request. This parameter, an Employee object, is added to the NavigationParameters' collection with a key that uniquely identifies it. The key can be used by the view model of the view you're navigating to retrieve a particular object.

C#

using Prism.Commands;using Prism.Regions;using StaffStuff.Common.Models;namespace StaffStuff.UI.ViewModels{ public class StaffDetailsViewModel : ViewModelBase { private Employee _employee; public Employee Employee { get => _employee; set => SetProperty(ref _employee, value); } private IRegionNavigationJournal _journal; public DelegateCommand GoBackCommand { get; } public StaffDetailsViewModel(IRegionManager regionManager) : base(regionManager) { GoBackCommand = new DelegateCommand(GoBack); } private void GoBack() => _journal.GoBack(); public override void OnNavigatedTo(NavigationContext navigationContext) { Employee = navigationContext.Parameters[nameof(Employee)] as Employee; _journal = navigationContext.NavigationService.Journal; } }}

Navigation back to the previous view is done using the navigation journal. The navigation journal also has a GoForward() method which can be used for forward navigation.

Module Registration

The modules in the sample application have to be registered with Prism's ModuleCatalog so they can be loaded by the app. This is done in App.xaml.cs.

C#

public partial class App{ ... protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<ServicesModule>(); moduleCatalog.AddModule<UIModule>(); }}

Conclusion

I've covered some aspects of Prism for WPF but there are a couple of areas I didn't highlight; like the different navigation strategies, Prism's event aggragator, and composite commands. This article has hopefully provided a basic platform from which you can delve deeper into other aspects of Prism. I recommend you take a look at the various Prism sample projects on GitHub and go through the documentation to get a wider view.

History

  • 1st July 2020: Initial post
Prism for WPF: A Basic Primer (2024)
Top Articles
Latest Posts
Article information

Author: Lidia Grady

Last Updated:

Views: 6337

Rating: 4.4 / 5 (65 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Lidia Grady

Birthday: 1992-01-22

Address: Suite 493 356 Dale Fall, New Wanda, RI 52485

Phone: +29914464387516

Job: Customer Engineer

Hobby: Cryptography, Writing, Dowsing, Stand-up comedy, Calligraphy, Web surfing, Ghost hunting

Introduction: My name is Lidia Grady, I am a thankful, fine, glamorous, lucky, lively, pleasant, shiny person who loves writing and wants to share my knowledge and understanding with you.