In Part 1 we laid the foundation of our Silverlight/Prism solution by setting up the following projects: Prism.Framework.Api, Prism.Framework.Web.Shell, Prism.Framework.Web.
In Part 2 we’re going to pick up where we left off and configure our Prism.Framework.Web.Shell project for use with the Prism framework. Before we begin, let’s quickly review the prerequisites.
Prerequisites
- You’re familiar with the Prism guidance concepts and understand why you would use it to build a Silverlight application. If you’re not familiar with Prism and it’s concepts, stop right now and read the documentation on MSDN. I’ll wait for you, I promise. :)
- You’re an experienced C#/VB software developer.
- You have some exposure (or a strong interest in learning) software design patterns such as Inversion of Control (IoC), Dependency Injection (DI), Model-View-Presenter (MVP), Model-View-ViewModel (MVVM).
- You have Visual Studio 2008 SP1 installed on your system.
- You have Silverlight Tools for Visual Studio 2008 SP1 installed on your system.
- You have some exposure working with Silverlight 2 (doesn’t need to be much – as long as you’re familiar with the structure of a Silverlight 2 project, XAML, and code behind).
Compiling The Prism Libraries
We have one more house keeping chore we need to take care of before we start with overhauling the stock Silverlight project with Prism – compiling the Prism source code. Again, this is a relatively trivial task.
NOTE: If you are running a 64-bit OS, substitute C:\Program Files (x86)\ for C:\Program Files\ in all file paths.
- Fire up VS2008 and open the following solution (assuming you placed the Prism folder in the location suggested in Part 1): C:\Program Files\Microsoft SDKs\Prism V2\CAL\CompositeApplicationLibrary.sln.
- Right-Click on the CompositeApplicationLibrary solution –> Rebuild Solution
- Change the Solution Configurations from Debug to Release and then Right-Click on the CompositeApplicationLibrary solution –> Rebuild Solution (this is just to rebuild the assemblies under Release configuration so they’re ready for when we need them).
- Now open up Windows Explorer and navigate to the following folder: C:\Program Files\Microsoft SDKs\Prism V2\.
- Create a new folder and rename it to: Bin
- Create two folders beneath the Bin folder: Debug, Release.
- Now navigate to: C:\Program Files\Microsoft SDKs\Prism V2\CAL\Silverlight\Composite.UnityExtensions\Bin\Debug
- Copy the following files to: C:\Program Files\Microsoft SDKs\Prism V2\Bin\Debug\
- Microsoft.Practices.Composite.dll
- Microsoft.Practices.Composite.Presentation.dll
- Microsoft.Practices.Composite.UnityExtensions.dll
- Microsoft.Practices.ServiceLocation.dll
- Microsoft.Practices.Unity.dll
- Microsoft.Practices.Composite.pdb
- Microsoft.Practices.Composite.Presentation.pdb
- Microsoft.Practices.Composite.UnityExtensions.pdb
- Microsoft.Practices.Composite.xml
- Microsoft.Practices.Composite.Presentation.xml
- Microsoft.Practices.Composite.UnityExtensions.xml
- Now navigate to: C:\Program Files\Microsoft SDKs\Prism V2\LIB\Desktop\CommonServiceLocation
- Copy the following file to: C:\Program Files\Microsoft SDKs\Prism V2\Bin\Debug\
- Microsoft.Practices.ServiceLocation.XML
- Now navigate to: C:\Program Files\Microsoft SDKs\Prism V2\LIB\Desktop\Unity
- Copy the following file to: C:\Program Files\Microsoft SDKs\Prism V2\Bin\Debug\
- Microsoft.Practices.Unity.xml
- Repeat the steps above, this time getting the files from the Release folder instead of Debug and copy them into: C:\Program Files\Microsoft SDKs\Prism V2\Bin\Release\
Configuring the Prism.Framework.Web.Shell project for Prism
Now that we have all the house keeping tasks out of the way, we can get into the good stuff – adding Prism to our solution. I learn best by doing first, understanding second, so I’m going to take that approach here. I’ll do my best to walk you through with screen shots and code snippets. Sound good?
Add New References and Code Files to the Prism.Framework.Web.Shell Project
- If you don’t already have the Prism.Framework solution opened in Visual Studio, go ahead and do that.
- Right-Click on the Prism.Framework.Web.Shell project and click Add Reference…
- Click on the Browse tab, then browse to the folder: C:\Program Files\Microsoft SDKs\Prism V2\Bin\Debug\
- Select the following files:
- Microsoft.Practices.Composite.dll
- Microsoft.Practices.Composite.Presentation.dll
- Microsoft.Practices.Composite.UnityExtensions.dll
- Microsoft.Practices.ServiceLocation.dll
- Microsoft.Practices.Unity.dll
- Click OK
- Add a new interface file named IShellView.
- Add a new class file named Bootstrapper.
Modifying Our New Files
-
Add the following method signature to the IShellView interface:
- Add the following using statements to the Bootstrapper.cs file:
- Microsoft.Practices.Composite.Modularity
- Microsoft.Practices.UnityExtensions
- Update the Bootstrapper’s signature to inherit from UnityBootrapper and then implement the abstract class.
- Now override the following protected methods:
- ConfigureContainer
- GetModuleCatalog
- Change the GetModuleCatalog method to the following:
protected override IModuleCatalog GetModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
return catalog;
}
- Change the ConfigureContainer method to the following:
protected override void ConfigureContainer()
{
Container.RegisterType<IShellView, Shell>();
base.ConfigureContainer();
}
- Change the CreateShell method to the following:
protected override DependencyObject CreateShell()
{
IShellView view = Container.Resolve<IShellView>();
view.ShowShell();
return view as DependencyObject;
}
Modify Page.xaml
- Right-Click on Page.xaml –> Rename
- Rename Page.xaml to Shell.xaml
- Right-Click on Shell.xaml –> View Code
- Rename the class from Page to Shell
- While you have the public partial class Shell open, implement the IShellView interface.
- Change the ShowShell method to the following:
public void ShowShell()
{
Application.Current.RootVisual = this;
}
- Shell.cs should now look like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Prism.Framework.Web.Shell
{
public partial class Shell : UserControl, IShellView
{
public Shell()
{
InitializeComponent();
}
#region IShellView Members
public void ShowShell()
{
Application.Current.RootVisual = this;
}
#endregion
}
}
- Add the following namespace declaration to the root UserControl element in Shell.xaml:
xmlns:regions="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation"
- Add a TextBlock element to root UserControl element in Shell.xaml and give it an arbitrary Text value (for instance, Hello World!). This is just to make sure our Shell view is loading when we run the application.
- Add the following ItemsControl element to the root UserControl element in Shell.xaml beneath the previously added TextBlock element:
<ItemsControl x:Name="MainRegion" regions:RegionManager.RegionName="MainRegion" />
Shell.xaml should now look like:
<UserControl x:Class="Prism.Framework.Web.Shell.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:regions="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="Hello World!" />
<ItemsControl x:Name="MainRegion" regions:RegionManager.RegionName="MainRegion" />
</Grid>
</UserControl>
Modify App.xaml.cs
- Right-Click on App.xaml –> View Code
- Change the Application_Startup method to the following:
private void Application_Startup(object sender, StartupEventArgs e)
{
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
-
Press F5 to run the application, you should see the following:
What Did We Just Do? And Why The Heck Did We Do It?
Alright, we just did quite a bit of work so let’s break it down piece by piece – I’ll skip over adding the Prism assemblies to the Shell project because that’s pretty self explanatory.
Immediately after adding the references to the Prism assemblies we added 2 new files to our project: IShellView.cs and Bootstrapper.cs. We’ll cover IShellView and the concept of using interfaces for views in a future post covering design patterns, so for now we’ll just say that creating and implementing the IShellView interface helps make our solution more testable.
The really important bits here are in Bootstrapper.cs, which is essentially the backbone of our Prism solution. In Bootstrapper.cs we overrode the CreateShell, ConfigureContainer, and GetModuleCatalog methods of the UnityContainer class from which we inherited – let’s take a closer look.
Modularity is one of the core design concepts of the Prism framework and a composite application. In the GetModuleCatalog method we’re essentially setting up the capability for our application to specify which modules will be used. However, as of right now all we’re doing is instantiating and returning an empty ModuleCatalog instance. In the next post we’ll be adding a module to the solution and will therefore need to add it to the ModuleCatalog - which we’ll do via static code.
Let’s now shift our focus to the ConfigureContainer method. In this method we’re registering components with the dependency injection container for retrieval at a later time. The IShellView interface is specified as the type that will be requested, and the Shell class is specified as the type that will actually be returned. At first glance this may not seem like a big deal, but if you think about it, it’s actually quite important. Since we’re using interfaces to request types, we’re not tied to any specific implementation and can therefore swap out functionality as needed. The interfaces will also come in handy when it’s time to write automated unit tests – therefore making our application easier to maintain and hopefully producing a higher quality product.
In the CreateShell method we resolved the Shell class by requesting an IShellView from the dependency injection container. Once we’ve resolved the Shell, we call the ShowShell method and then return the Shell as a DependencyObject. There’s nothing too magical happening in this method, but I’d like to note that creating the Shell in the bootstrapper class aids in testability since we can mock the Shell in a unit testing framework.
Moving along to the Shell, the first thing we did was rename Page.xaml to Shell.xaml. This is merely for convention and we could have just as easily left Page.xaml alone. We then implemented the ShowShell method from the IShellView interface which sets the Application’s RootVisual equal to the instance of the Shell. In the Shell.xaml file we then added the regions namespace so we could add the ItemsControl which is the container control for our MainRegion region. Of course, we don’t have any views to inject into the MainRegion right now - but we’ll be adding one in the next post.
Finally, we modified the Application_Startup method of the App class to instantiate an instance of our bootstrapper class and call it’s Run method. The Run method essentially starts the process of configuring the container, configuring the region mappings, creating the shell, and initializing the modules. These activities directly relate to the methods we overrode when we created the Bootstrapper class which is precisely why we needed to override the default implementation of those methods. One last thing to point out. If you have a keen eye, you probably noticed the code we replaced within the Application_Startup method is nearly identical to the code we placed within the ShowShell method of the Shell class. By doing this we’re simply removing the responsibility of displaying the Shell as the application’s visual root from the App class and placing it in the hands of the Shell class which is more aligned with using the bootstrapper process.
This was a long but essential post and we’re finally ready to begin creating modules and add “real” functionality to our app. And that’s exactly where we’ll pick up in Part 3.