After 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 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 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
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 "http://www.codeplex.com/CompositeWPF" 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()
{
this._container.RegisterType<IProjectService,
ProjectService>(new ContainerControlledLifetimeManager());
this._container.RegisterType<IProjectController,
ProjectController>(new ContainerControlledLifetimeManager());
this._container.RegisterType<IProjectTreeView, ProjectTreeView>();
this._container.RegisterType<IProjectTreeViewModel, ProjectTreeViewModel>();
}
#endregion Private Methods #region IModule Members
public void Initialize()
{
RegisterViewsAndServices();
IProjectController controller = this._container.Resolve<IProjectController>();
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 its predecessor.