Building applications with Prism
Prism framework helps in designing and building flexible, loosely coupled, easy-to-maintain business apps that run on WPF (and many other desktop application frameworks). This blog post serves as a guide to build composite desktop applications using the Prism framework.
Modular development – different modules, different teams, one application
Enterprise desktop applications demand excellent user experience and rich customizable feature sets. However, companies are challenged with time-to-market when dealing with the continuously changing requirements. Based on our experience, Prism helps businesses overcome this.
Typically, business apps can be conceptualized as a combination of different modules bundled together. Prism allows us to design, develop and test modules independently by different teams in parallel. These modules can then be integrated in your application using different practices followed in Prism.
Figure 1. Module initialization in Prism
Of the many options available to integrate modules in your application, below are the few techniques we have used while working with Prism.
Discover modules on disk
Initially, Prism’s DirectoryModuleCatalog class (customizable) scans DLL’s in a specified directory. As a prerequisite, each of the modules should implement the IModule interface. These classes are picked during the directory scan and loaded into the application.
Load modules (OnDemand attribute for IModule implementation)
OnDemand = false – initialized when discovered.
OnDemand = true – initialized on demand.
This is useful when modules need to be loaded based on rules/permissions for the logged-in user. Also, this avoids the overhead of loading modules at the start of the application, which makes the application faster. Prism’s ModuleManager provides methods to load modules on demand.
Initialize modules
When a module is loaded, data initializing data, making service calls, registering types and much more can be achieved using the OnInitialized and RegisterTypes methods of the IModule class.
Visual Architecture – Shell, Regions and Views
Shell – Application’s main window; hosts all the different modules
Region – Prism’s entity to logically separate the application layout
Views – WPF UI controls and components
Logical separation of concerns – Regions
Prism requires the shell window to be divided into named regions. In our application, we had regions to host ribbons, data in tabular format, preview and navigation panes.
Region adapters and managers
Region adaptors and managers enable different views to be properly rendered into different regions in the shell window. Prism avoids the overhead of writing code to render controls at specific locations.
Typical controls used in the shell window would be,
- Content control – hosts different types of controls
- Ribbon bars
- Tab controls – tabular data representation
- Tree views – data represented in tree format and more.
IActiveAware, INavigationAware classes
When following design patterns such as MVVM, view models need to be notified when its view (rendered into a specific region) becomes active or inactive. Prism provides these capabilities with the IActiveAware and INavigationAware interfaces. With these implementations, data flow, data initialization, service calls and much more can be controlled from within the view models without additional overheads.
Communication – Composite, Delegate commands, Messenger service
Prism provides the following implementations of WPF’s RoutedCommand,
Delegate command – takes a method pointer to invoke
Composite command – a collection of delegate commands
Solution commanding – Bind to globally available commands
Each module can define a set of global commands which will be bound to actual controls in different regions (prominently, in the ribbon region). View models implement these commands contextually (different view models can have different implementations for a global command). Using IActiveAware features, view models can register to these commands, which completes the circle; When the control action is invoked in the view, the function in the view model is executed automatically.
Shared services – Messenger
Prism enables communication amongst entities (views, view models, services etc.) within, and across modules by using Shared services. In our project, we have implemented a Messenger service shared across our entities.
Any entity requesting an action sends across a message with defined parameters and a unique token (to identify the source and/or target). Messenger service scans all registrations for this message type based on message parameters and invokes the target action accordingly.
Concluding note:
Prism bundles together an implementation of various design patterns that have enabled us to build a robust, well-structured and maintainable desktop application using WPF. We have immensely benefited in building Proarc Windows Client with Prism. If you are looking to build desktop applications or planning to migrate legacy applications to a modern user interface, the key is to pick and choose relevant features from the Prism framework to build your composite application.
Author:
Muraliprasad Janakiraman is a Lead Software Engineer and has been contributing in Proarc R&D for many years.