Introduction
In this article I will try to use couple of different pieces of functionality which PRISM offers us to implement MVVM. I am trying my best to start it from scratch for newbie. I prefer if you are really interested please try to develop your sample MVVM Silverlight application while reading the article side by side in order to understand it in detail.
I strongly suggest if you don’t have basic understanding of UI pattern please go through this article once. It will help you to understand basic concept of MVVM Basics_UIDesignPattern.aspx and other patterns.
In this post I am developing a clean MVVM pattern architecture for "Hello world" Silverlight application. We will be using the Microsoft Unity and Prism libraries for modularity and dependency injection. I am not exploring all feature which PRISM offer us but explaining basics which every should know before developing Silverlight application using PRISM.
Prism is more than MVVM
PRISM can be interesting to you because it's more than an implementing MVVM framework. Although, a part of it can be considered in fact an MVVM framework (the NotificationObject
, the EventAggregator
and the Command objects are all examples of that), however it offers much more. It allows you to create Composite Application of multiple loosely couples "Modules". It has a very flexible and extensible navigation framework (Region Navigation), offers integration with IoC containers (notably Unity and MEF) and a ton of other features. EventAggregator
can be used to communicate among different module.
If you wanted to explore more about PRISM here is the link for that http://www.sparklingclient.com/prism-silverlight/
In this article I am developing "Hello World" Silverlight application using Prism. You will see after using PRISM it will increase modularity and will improve loose coupling and separation of concern which in turn improve testability, Maintainability and extendibility with minimum effort. So here is the UI architecture of the application which I am explaining below.
In this article we will have two different views. So idea is to develop "hello world" application where "Hello" text will represent one view and "World" will represent another view and finally both view will reside inside Shell (Under specified region). I am keeping this application very simple since my objective is to familiarize you with Prism and MVVM.
Let’s start from scratch.
Prerequest
- Visual studio 2010 and Silverlight 4 (http://www.silverlight.net/downloads you can get both)
- You can download PRISM 4 lib from http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=4922
How to Install PRISM
- Click on download and run Prismv4.exe in order to extract PRISM framework in your computer.
- It will ask you to provide location where you wanted to save it and then it will download all source file into that folder.
- You can see there so many files it download however we need few dll in our application.
- I recommend you to copy "Silverlight" folder to your project location and change its name to "Lib".
- You can find "Silverlight" folder in following location – {Download folder}\Bin\Silverlight
So now you have PRISM DLL into Lib folder at your project location.
Click -> Visual Studio 2010 -> File-> New Project -> Silvelright Project (Give name PrismSample) and browse to the location where you kept your "Lib" folder . Its "D:\SilvelightPrismDemo\" in my case.
Click ok and maintain following option. (Its default)
After clicking ok button you will get solution by name PrismSample which has two project in it. One is Silverlight project (PrismSample) and another is Web project (PrismSample.Web).
Next step is to delete MainPage.xaml and add Shell.xaml (i.e Silverlight User Control) to your Silverlight Project.
This page (Shell.xaml) will act as master page in our application and we will define different region into this view where each region will bind with specific view.
Shell.xaml
Inside the Prism Framework, the overall layout and style can be defined inside Shell. The Shell is equivalent to an ASP.NET MasterPage. Both allow you to define content regions as placeholders for the views you define. Both allow you to define visual styles and layout which will be applied to the views and which are displayed within the content regions you define. Whereas a MaterPage
uses ContentPlaceHolder
objects, Prism uses regions. A region is nothing but an attached property for specific xaml objects (ContectControl in this article) where you can inject your views to display them.
However In Silverlight you will only have a single Shell page. Your Shell has been set as the value of Application.Current.RootVisual
Hence you can set it only once which in turn force to us to have single Shell. However in case of WPF, prism is more flexible since it allows us to declare more than one shell when your application uses more than one window.
Let’s dig into code " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> initially your Shell.xaml should look like this.
<UserControl x:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:Design d:Design>
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
</UserControl>
Next step is to add Prism reference to our project. Add following dll from Lib folder to your Silverlight (PrismSample) project.
Once added these dll we can bring Prism namesapces in to xaml. So you need to add following name space into your shell.xaml.
xmlns:Regions=
"clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism"
After adding this namespace you can define your region control which in turn can handle view. In this example I am using ContentControl as region container control. Some time we use ItemControl also to act as region. These ContentControl will act as PlaceHolder for our view. In order to get Region functionality into our Shall.xaml we have to add following name space. It allows us to define region in Content Control.
xmlns:Regions=
"clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism"
Here is the updated Shell.xaml
<UserControl x:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Regions=
"clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism"
mc:Ignorable="d"
d:Design d:Design>
<Grid x:Name="LayoutRoot" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="Sample MVVM Hello World Application"
FontSize="16" HorizontalAlignment="Center" Grid.Row="0" Grid.ColumnSpan="2"/>
<ContentControl Grid.Row="1" Grid.Column="0" x:Name="HelloRegion"
Regions:RegionManager.RegionName="HelloRegion" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ContentControl Grid.Row="1" Grid.Column="1" x:Name="WorldRegion"
Regions:RegionManager.RegionName="WorldRegion" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</UserControl>
Once it’s done now we will add Bootstrapper.
Bootstrapper
The Bootstrapper is always be a starting point whenever we developing any application for Prism. The basic purpose of our Bootstrapper is to initialize our Shall and register all the types you’ll need for Dependency Injection (DI). Bootstrapper will responsible to load and start our application. Name itself explain its functionality. There are 2 common tasks that you’ll do every time you create a new Boostrapper:
- Create Shell
- Setup ModuleCatalog
Let’s create our Bootstrapper. Add new .cs file to Silverlight project and name it as Bootstrapper.cs. Here is the code which you need to have in Bootstrapper class
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
Shell rootShell = new Shell();
Application.Current.RootVisual = rootShell;
return rootShell;
}
protected override IModuleCatalog CreateModuleCatalog()
{
base.CreateModuleCatalog();
}
}
And add following namespace to it in order to resolve "UnityBoostrapper"
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Prism.Modularity;
Once this is done lets understand its code. CreateShell()
method is responsible for registering shell. CreateModuleCatalog()
method is used to register module which require Dependency Injection. In this case we have only two modules. One module for each view. Once we have module ready we will come back to this method. Now if you build a solution you will get error. Since we have not instantiated Bootsrapper yet and our App.xaml.cs class still has reference to MainPage.xaml. Modify App.xaml.cs by following code.
private void Application_Startup(object sender, StartupEventArgs e)
{
Bootstrapper bootStrapper = new Bootstrapper();
bootStrapper.Run();
}
Now build your solution It should build without any error. If you run you application it will run and you will see empty page like this.
So our Shell is ready so now next job is to create views, viewmodels and models.
Right click on solution file (PrismSample) and add new project (Silverlight Class library).
Provide name "ModuleHello" and similarly add another Silverlight class library project by name "ModuleWorld".
- So our solution now has 4 projects.
- One Silverlight Application project
- Two Silverlight class lib project (ModuleHello and ModuelWorld)
- One Web project (Hosting silverlight xap file in aspx page)
What do we mean by Module?
Module can be views, collection of related components, such as features, views, or business logic. So In our example Hello is one module which further has view, viewmodel and model specific to "Hello" functionality. Similarly we have different module for "World" functionality. Once these modules ready we will add them to catalog in Bootstrapper
class. I will explain it later. Let’s first complete "Hello" module.
Now open your ModuleHello project and add HelloModule.cs file. Before going forward you need to add reference to Prism DLL from Lib folder. Add Microsoft.Practices.Prism.dll and Microsoft.Practices.Unity.Silverlight.dll as reference from Lib to ModuleHello project.
Also create "View" folder inside HelloModule project and then add Silverlight control by name "HelloView". Also add IHelloView.cs class to same folder.
Then create another folder by name "ViewModel" inside HelloModule project then add "HelloViewModel.cs" file and also add IHelloViewModel.cs file inside ViewModel folder. Make sure HelloView class implement IHelloView interface and the HelloViewModel
class should implement the IHelloViewModel interface.
IHelloView.cs
using ModulHello.ViewModel;
namespace ModulHello.View
{
public interface IHelloView
{
IHelloViewModel Model { get; set; }
}
}
IHelloViewModel.cs
using ModulHello.View;
namespace ModulHello.ViewModel
{
public interface IHelloViewModel
{
IHelloView View { get; set; }
}
}
HelloView.xaml.cs
using System.Windows.Controls;
namespace ModulHello.View
{
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
#region IHelloView Members
public ViewModel.IHelloViewModel Model
{
get
{
return this.DataContext as ViewModel.IHelloViewModel;
}
set
{
this.DataContext = value;
}
}
#endregion
}
}
Now add following code to your HelloModule.cs class.
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Unity;
using ModulHello.View;
using ModulHello.ViewModel;
namespace ModulHello
{
public class HelloModule : IModule
{
#region IModule Members
private IRegionManager regionManager;
IUnityContainer container;
public void Initialize()
{
this.RegisterViewsAndServices();
if (this.regionManager.Regions.ContainsRegionWithName("HelloRegion"))
{
this.regionManager.Regions["HelloRegion"].Add(
this.container.Resolve<IHelloViewModel>().View);
}
}
public HelloModule(IRegionManager regionManager, IUnityContainer container)
{
this.container = container;
this.regionManager = regionManager;
}
#endregion
protected void RegisterViewsAndServices()
{
this.container.RegisterType<IHelloView, HelloView>();
this.container.RegisterType<IHelloViewModel, HelloViewModel>();
}
}
}
Our module class (ModuleHello
) is inheriting the IModule
interface. Here we are registering a view in the region manager using Interface for view and viewmodel. The IModule
interface will tell the application to register the View as a Module which could be loaded into MainRegion
The region manager and the DI container will be passed into the constructor and the Initialize method will be called. First we register these modules types that we will use. Then we add the Hello view model to the region in the shell view named "HelloRegion".
Let’s understand this code: RegisterViewsAndServices()
This method register view and viewmodel with their interface and can be used to create the instance of implemented class.
Here is the code for HelloView.xaml.cs
<usercontrol x: ="" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:ignorable="d" d:design d:design>
<grid x:name="LayoutRoot" background="White">
<textblock text="{Binding HelloText}" fontsize="26"
horizontalalignment="Center" verticalalignment="Center" ="">
</textblock></grid></usercontrol>
<UserControl x:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:Design d:Design>
<Grid x:Name="LayoutRoot" Background="White" >
<TextBlock Text="{Binding HelloText}" FontSize="26"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
Here is code for HelloViewModel.cs
namespace ModulHello.ViewModel
{
public class HelloViewModel : IHelloViewModel
{
public HelloViewModel(View.IHelloView view)
{
this.View = view;
this.HelloText = "Hello (From hello view)";
this.View.Model = this;
}
public string HelloText { get; set; }
#region IHelloViewModel Members
public View.IHelloView View
{
get;
set;
}
#endregion
}
}
Here we are binding "HelloText
" property to View TextBlock and displaying it to UI. In this case we are not using separate model however you can define separate class for model and which has different property and then define its reference as a public property in ViewModel class which in turn bind model property to UI view.
Now our HelloModule is ready to use which has its own view and viewmodel. However there is no link between Shell and this module. For that you have to add reference of "HelloModule" and "WorldModule" to your Silverlight project (PrismSimple) and then add following code in Bootstrapper
class
protected override IModuleCatalog CreateModuleCatalog()
{
ModuleCatalog modules = new ModuleCatalog();
modules.AddModule(typeof(ModulHello.HelloModule));
return modules;
}
Now if you run your application you will see following screen. Here you can see second row and first column of your shell is displaying "HelloView". Hence half part of our application is completed
Now you can feel the advantage of using Prism. It really helps us to implement MVVM without any extra effort. Prism also supports other pattern like Commanding, event initialization which I will cover in future article. Now we can ask different developer to work simultaneously on different module and within module also we have separation of duties. For example View can be develop by UI designer and then get data from viewModel which can be developed from different developer. So we have a nice decoupling, and we can test our ViewModel code by writing UnitTest cases. Now we realize that Prism help us to develop this application with loss coupling and separation of concern which in turn improve testability, Maintainability and extendibility with minimum effort.
Let’s complete WorldModule.
Similar to HelloModuel this module will also have same folder "View" and "ViewModel" and will also have same file with different name. It’s "WorldView
", "IWorldView" in "View" folder and "WorldViewModel" , "IWorldViewModel
" in "ViewModel" folder. Also add "WorldModule.cs" file in WorldModule project.
Also add Microsoft.Practices.Prism.dll and Microsoft.Practices.Unity.Silverlight.dll (PRISM dll) reference to WorldModule.
After adding these files you solution explorer window will look like this.
Here is the code for "WorldModule.cs" file
using Microsoft.Practices.Unity;
using Microsoft.Practices.Prism.Regions;
using ModuleWorld.View;
using ModuleWorld.ViewModel;
using Microsoft.Practices.Prism.Modularity;
namespace ModuleWorld
{
public class WorldModule : IModule
{
#region IModule Members
private IRegionManager regionManager;
IUnityContainer container;
public void Initialize()
{
this.RegisterViewsAndServices();
if (this.regionManager.Regions.ContainsRegionWithName("WorldRegion"))
{
this.regionManager.Regions["WorldRegion"].Add(
this.container.Resolve<IWorldViewModel>().View);
}
}
public WorldModule(IRegionManager regionManager, IUnityContainer container)
{
this.container = container;
this.regionManager = regionManager;
}
#endregion
protected void RegisterViewsAndServices()
{
this.container.RegisterType<IWorldView, WorldView>();
this.container.RegisterType<IWorldViewModel, WorldViewModel>();
}
}
}
IworldView.cs
using ModuleWorld.ViewModel;
namespace ModuleWorld.View
{
public interface IWorldView
{
IWorldViewModel Model { get; set; }
}
}
IWorldViewModel.cs
using ModuleWorld.View;
namespace ModuleWorld.ViewModel
{
public interface IWorldViewModel
{
IWorldView View { get; set; }
}
}
WorldView.cs
<UserControl x:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" >
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding WorldText}" FontSize="26"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
WorldView.xaml.cs
using System.Windows.Controls;
namespace ModuleWorld.View
{
public partial class WorldView : UserControl, IWorldView
{
public WorldView()
{
InitializeComponent();
}
#region IWorldView Members
public ViewModel.IWorldViewModel Model
{
get
{
return this.DataContext as ViewModel.IWorldViewModel;
}
set
{
this.DataContext = value;
}
}
#endregion
}
}
WorldViewModel.cs
namespace ModuleWorld.ViewModel
{
public class WorldViewModel : IWorldViewModel
{
public WorldViewModel(View.IWorldView view)
{
this.View = view;
this.WorldText = "World (From World view)";
this.View.Model = this;
}
public string WorldText { get; set; }
#region IWorldViewModel Members
public View.IWorldView View
{
get;
set;
}
#endregion
}
}
After these changes build and run your application, you will see following output.
Done " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> We have devloped our hello world application using PRISM.
This is all you need to get started building modularized and scalable Silverlight applications using Prism and Unity. The possibilities with this architecture are endless and it’s a very strong architecture to begin with. Thank you for reading " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> I hope you like it and if you like it please gives rating also. It motives me to write such stuff " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" /> .
Please feel free to comment and share your thoughts. I am open and willing to discuss anything " src="http://www.codeproject.com/script/Forums/Images/smiley_smile.gif" />
Reference