Derek Lakin

Migrating a Legacy WPF Application to Prism - Part 2

Prism.FirstView.StructureAfter my initial successes in moving to a Prism-style bootstrapper in Part 1 it was time to move onto the next step. But in the case of a legacy application, what’s the next step?

Application Structure Overview

Our legacy application consists primarily of a WPF user interface, a WCF service that provides access to a SQL Server database and a few support libraries. One of those support libraries provides some classes that deal with the interaction between the user interface and the WCF service and is my prime candidate for making some improvements using the Prism guidance.

There are two main entities, one that relates to a Project that is handled by the application and one that relates to a Pages, which make up the contents of a Project. So these seemed to me to be good choices for modules. But, the existing infrastructure merges the roles of Service and ViewModel into a single class, which I’d like to separate.

So, my plan (so far at least) is to create Prism Modules for the Project and Page and to create the relevant Views, ViewModels, Services, and Controllers that are required to implement the functionality. Rather than having to fully re-write the underlying code that handles UI/WCF Service interaction, I’m planning on writing a Prism-facade that will simply call out to the existing infrastructure, which I can eventually replace as time progresses. This gives me the testability advantages of Prism without having to rip out the heart of the application and re-write it completely, which would be a costly and time consuming task.

Getting Started

Prism.FirstView For my next step I’m focusing on the Project aspects of the application, so (as you can see in the screen shot above) I’ve created a Project module containing the ProjectModule, a ProjectService, ProjectTreeView, ProjectTreeViewModel and a ProjectController, which will handle the interactions between the different views of a project (when I get that far).

Each of these classes implements an interface, which enables me to easily mock these implementations to test specific aspects of the application. At this stage my implementations are very simple in that they just initialise the relevant Views, ViewModels, etc. The View itself is simply a UserControl that contains a root Grid element with a pink background. The important thing is to get it loaded and displayed first.

Implementation Specifics

In the application’s shell, the existing user control was removed and replaced with the following region definition.

ContentControl x:Name="_navigation" cal:RegionManager.RegionName="{x:Static inf:RegionNames.Navigation}" DockPanel.Dock="Left" />

The "cal" XML namespace resolves to "" and the "inf" XML namespace resolves to an infrastructure assembly that contains constants for the region names.

The ProjectController class retrieves the relevant region that is the destination of the View and adds the View for the associated ViewModel to that region and then activates it as shown in the following code example.

public void Run() { IRegion region = this.regionManager.Regions[RegionNames.Navigation]; region.Add(this.projectTreeViewModel.View); region.Activate(this._projectTreeViewModel.View); }

The call to the Activate method is essential if you want to be able to see your View. Until the Activate method is called, you will not be able to see your View.

The Run method of the ProjectController is called in the Initialize method of the ProjectModule after registering the required views and services, as shown in the following code example.

#region Private Methods private void RegisterViewsAndServices() { // NOTE: The ContainerControlledLifetimeManager ensures that the same // instance is returned by any call to Resolve or ResolveAll. this.container.RegisterTypeIProjectService, ProjectService>(new ContainerControlledLifetimeManager()); this.container.RegisterTypeIProjectController, ProjectController>(new ContainerControlledLifetimeManager()); this.container.RegisterTypeIProjectTreeView, ProjectTreeView>(); this.container.RegisterTypeIProjectTreeViewModel, ProjectTreeViewModel>(); } #endregion Private Methods #region IModule Members public void Initialize() { RegisterViewsAndServices(); IProjectController controller = this._container.ResolveIProjectController>(); controller.Run(); } #endregion

As you can see in the screen shot above, apart from writing all the tests, putting some simple content in the ViewModel, a bit of work on the infrastructure and doing a lot of head scratching, that’s it! Granted it’s just a pink rectangle at the moment, but I now have a View that is loaded into the right place.

My next step is to put some useful UI code into the View and try to gradually incorporate some of the visual functionality of it’s predecessor.