RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Build Composite WPF Applications : Page 2

Build loosely-coupled, maintainable WPF applications using the Composite Application Guidance patterns and practices.

Composite Application Structure
Your Composite WPF application should always have a standard structure. You will have a WPF Application project (EXE) called the "shell project." The contents of the shell project should be mostly ignorant of the implementation details of the specific business problem the application addresses. It just provides the top level structure for the application, the main window structure, and the initialization code to set up the execution environment. The shell project contains the Shell—the main application window—and a bootstrapper class that gets everything rolling. It may also contain shared resources and styles for the entire application, as well as shared services. However, you can also factor those out into other shared assemblies.

Modules provide most of the functionality of the application. Modules are the top-level building blocks of composite apps. Technically you can have more than one module class in a single assembly, but the recommended approach is to have a separate class library for each module. A module assembly contains a module class that acts as the entry point or initializer for the module. It also contains all the view definitions and supporting code for whatever portion of the business functionality that module encapsulates.

Figure 1 shows how the shell and modules contribute to create the runtime application. As the shell executable starts, the bootstrapper takes care of initializing the environment and creates and displays the Shell main window. The bootstrapper sets up any shared services for the application and then loads the modules that the application is composed of. Each module then initializes its own views and services, and adds its views to known locations in the shell, called regions.

Figure 1. Composite Application Startup Process: The figure shows the relationship between the shell and the modules that create the runtime application.
Figure 2. AppointmentManager Application Solution: This Solution Explorer view shows the files in the AppointmentManager solution.

Figure 2 shows the project structure for the application I will cover in this article. The three class library projects under the CAL solution folder are the libraries that ship with the Composite Application Guidance for WPF (collectively called CAL). The AppointmentManager project is the shell project. The Appointment.Infrastructure is a shared library referenced by both the shell project and the module projects, and holds shared types such as constants, event types, entity definitions and interfaces. The application is composed of two modules, one for viewing appointments and one for editing appointments.

In any project that uses CAL, you will generally want to reference all three CAL libraries: Composite, Composite.Wpf, and Composite.UnityExtensions.

To create your bootstrapper, you can simply inherit the UnityBootstrapper base class from CAL:

   // C#
   class AppointmentBootstrapper : UnityBootstrapper
      { ... }
   ' VB
   Public Class AppointmentBootstrapper
      Inherits UnityBootstrapper
   End Class
When you do that, CAL will take care of setting up Unity as the dependency injection container, and it will also set up the infrastructure for using several other CAL services covered later in this article, specifically the module loading service, the region manager service, and the event aggregator service.

You will need to override two methods in your bootstrapper: the CreateShell method and the GetModuleEnumerator method.

   // C#
   protected override DependencyObject CreateShell()
      Shell shell = new Shell();
      return shell;
   protected override IModuleEnumerator 
      return new ConfigurationModuleEnumerator();
   ' VB
   Protected Overrides Function CreateShell() _
      As DependencyObject 
      Dim shell As New Shell()
      Return shell
   End Function
   Protected Overrides Function GetModuleEnumerator() _ 
      As IModuleEnumerator
      Return New ConfigurationModuleEnumerator()
   End Function
The CreateShell function constructs an instance of the main window type, shows it, and returns the reference so the base class can configure the region manager service for that window. The GetModuleEnumerator method simply returns an instance of your module enumeration service. A module enumerator is a class that tells the module loading service which modules to load.

I'll cover module loading in more detail shortly, but CAL has three module enumerators you can use out of the box. For most scenarios, you can just use the enumerator classes provided in CAL. The sample application uses the ConfigurationModuleEnumerator, which uses information in the config file to determine what modules to load. CAL also includes a DirectoryLookupModuleEnumerator, which scans a directory to find all the modules in assemblies in that directory; and a StaticModuleEnumerator, which allows you to programmatically tell the module enumerator which modules to load. You can find examples of using those module enumerators in the QuickStarts that come with the Composite Application Guidance for WPF.

For the appointment manager sample application, you also need a data service that provides appointment data to all the loaded modules. I chose to implement that service in the Appointment.Infrastructure class library, and get it loaded at the first opportunity in the bootstrapping process, immediately after the dependency injection container gets initialized by the base class. The UnityBootstrapper class provides a hook for you to either provide your own container or do additional initialization steps through an override of the ConfigureContainer method, like this:

   protected override void ConfigureContainer()
         new ContainerControlledLifetimeManager());
   ' VB
   Protected Overrides Sub ConfigureContainer()
      Container.RegisterType( _
         Of IAppointmentDataService, _
         AppointmentDataService) _
         (New ContainerControlledLifetimeManager())
   End Sub
The preceding code uses the RegisterType method on the IUnityContainer interface (exposed to me through the UnityBootstrapper.Container base class property) to register the type with the container so that classes in the loaded modules can obtain it from the container and use the same service instance. The ContainerControlledLifetimeManager ensures the instance references handed out by the container follow the singleton pattern.

After defining the bootstrapper class, you need to slightly modify to the way the application starts up compared to normal WPF applications. First you remove the StartupUri property from the Application element in App.xaml.

   <Application x:Class="AppointmentManager.App"... 
Then you add an override for the OnStartup method in the code behind of the Application class to create the bootstrapper, and then call Run on the base class.

   // C#
   protected override void OnStartup(StartupEventArgs e)
      new AppointmentBootstrapper().Run();
   ' VB
   Protected Overrides Sub OnStartup(ByVal e _
      As StartupEventArgs)
      CType(New AppointmentBootstrapper(), _ 
   End Sub

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date