Background
Continuing to my Prism series 1 of n, in this series,
I am going to talk about a few more interesting concepts like Modules and Views.
Modules
In this article, we will talk about how to take other views/logic and broken down into a small pieces called Modules and use them in your Prism application.
We gonna start-up by talking about What a Module is, then registering Modules, loading Modules and then we will talk about how to initialize a Module.
What is a Module?
You can think Module as a building block for our Prism application. It is a package that contains all the functionalities and resources required for our application.
Prism provides a support for run-time Module management in our application, in which each Module can be developed and tested independently. Prism application loads
the Modules as and when they are required. Before moving forward, let’s have a look at how our application was architected inside Visual Studio prior to the concept of Modules.
In above figure, we have single solution and inside that we have single project and within that project we have every single view, all our services, and all our business logic.
I mean, single project contain each and everything, which is required for my project. Now, we want to identify, how we can break down our application into smaller pieces
called Modules as shown in below figure:
Now, if you look at our solution, you will see that we still have that main project with Shell and Regions in it whereas all the major functionality is defined outside the main project.
From above figure, we can see that each Module is independent of each other. Here each module can be single class library or a DLL or a XAP, that includes a collection of other
related components.
Each Module has a central class that implements the IModule interface and this is what identifies as a library to a Prism library. This interface has a single method
in it called Initialize, which is responsible for initializing a Module and integrating it to your Prism application and all can talk later on with this module.
Creating a Module
In order to create a Module, one needs to add below reference to the project:
- Microsoft.Practices.Prism.dll
Next we need to add a class library and inherit IModule
interface, as:
public class MyModule : IModule
{
public void Initialize()
{ }
Registering and Discovering Modules
All the Modules need to be loaded at runtime in particular application. ModuleCatalog contains information about all Modules to be loaded, it knows the location and it knows the order in which they are going to be loaded. It even knows that a particular module is dependent on another Module. I hope, one thing is clear that we need to register our Module to ModuleCatalog via code, XAML or by a configuration file.
Loading Module
Well, next process in Module lifetime is loading a Module. All of the assemblies that contain the Module need to be loaded into the memory. This can be from disk, directory or the web link (only for SL). Prism allows us to control, when to load a module. They can be loaded as soon as possible, which is known as ‘When available’ or can be loaded, when a application need them, which is known as ‘On-demand’.
Guidelines for loading the Module
It is always good to decide prior that when we want to load our Module. Never forget to think on below points:
- Is it required to run the application – If it is, then it must be downloaded with the application and has to be initialized when the application loads
- Always used – If it is, it can be downloaded in the background, when it becomes available
- Rarely used – If this is the case, then it can be downloaded in the background and can be initialized on demand
Initializing Module
The last phase in the Module’s lifecycle is initializing Modules. IModule.Initialize()
does this for us. In this method, we write a code to register our types, we can even subscribe to services or events. We can even work with shared services. We can also compose our view into the Shell. So, basically the
Initialize()
method is where you can put all your code which make your Module ready for consumption in our Prism application.
Views
Most of the applications require some
type of interface with user can interact with. Here we will talk about Views,
which will provide the interface to the user and will also see, how to add
views to the Prism application. We will touch upon, what a View is, about View
Composition, View Discovery and View Injection.
What is a View?
In layman term, View is simply the
interface that user uses to interact with the application. But in Prism
application, this concept is bit different. In Prism, View is only the portion
of user interface. It is a smaller unit of user interface, which encapsulates a
piece of functionality and is coupled with other parts of user interface. Let’s
take an example of Outlook. In Outlook, we have toolbar at top and a navigation
bar on left side. If you will look closely into it, you will find that, both of
these parts have no dependency. For example, outlook toolbar can probably have
its own View and has no dependency on other parts of the application. Its main
function is to send messages and the actions which are needed to be performed.
And the same is true for navigation area, the email list, the content area and
even for a status bar. So, we can see that a Outlook is composed of multiple
Views.
Composite View
In Prism, a View can be made up of
multiple Views, which is also called as Composite View. Here Composite View can
be think of Parent with, having its child view, again sub child view, etc. View
can be composed of User control, Page, Data Template, etc. Basically, it can be
anything, which can be used to display information to the user. We can also
have multiple instances of Views. For example, tab control with a number of
tabs showing the same view.
Please keep in mind, while creating a
View, design patterns are not required. There is nothing specific to the Prism
library that requires you to use the design pattern to bind a View. But one can
use any of the available design patterns for creating a View.
Sample code for implementing Views
Let’s create a View, by adding a user
control MenuBarView
:
<UserControl>
<Button>MenuBar</Button>
<UserControl>
Please note, here I am not using any
type of design pattern namely MVC, MVP to create my views. Next thing, we need
to do is to register our View with container. In order to do that, you need to
open your Module class. In the Initialize()
method, we need to register our
view with our container. And of course, in order to do this we need a
reference to a container.
Likely, it is pretty easy again. Let’s start by adding reference of
Microsoft.Practices.Unity.dll. Then we need to add a constructor for our Module
class as:
public class MyModule : IModule
{
IUnityContainer _container;
public MyModule(IUnityContainer container)
{ _container = container ;}
public void Initialize()
{ _container.RegisterType<MenuBarView>(); }
}
So, what’s happening in above code.
Here, whenever Module is created, Prism is identifying a container, which is
Unity container here. Once the container is identified, our Initialize()
method
is called and we start registering our types with the container So, once our
types are registered with container, we can start using them in the module.
Composing Views
So, next step is composing our
views. Here, I am going to use MVVM. Most of us are already aware, that in
MVVM, we start with creating interfaces. Here also, I am creating public interfaces named
IView
and IViewModel
. Now,
Let’s create a View, by adding a user
control MenuBarView
:
public interface IView
{
IViewModel ViewModel {get; set;}
}
and
public interface IViewModel
{
IView View {get; set;}
}
Please remember, in MVVM, ViewModel can
never have direct reference of View and that’s the reason, I created IView
. Now
add another interfaces for our View and ViewModel named IMenuBarView
and
IMenuBarViewModel
, as:
public interface IMenuBarView: IView { }
Now, go to the View (MenuBarView
) code
behind implement the interface IMenuBarView
, as:
public partial class MenuBarView: UserControl, IMenuBarView
{
public MenuBarView()
{
InitializeComponent();
}
public IViewModel ViewModel
{
get{ return (IMenuBarViewModel)DataContext;}
set{ DataContext = value; }
}
}
Next thing, we need to do is, go to IMenuBarViewModel
and implement
IViewModel
as:
public interface IMenuBarViewModel: IViewModel
{
public MenuBarView()
{
InitializeComponent();
}
public IViewModel ViewModel
{
get{ return (IMenuBarViewModel)DataContext;}
set{ DataContext = value; }
}
}
Now, we will create a ViewModel named
MenuBarViewModel
,as:
public class MenuBarViewModel: IMenuBarViewModel
{
public MenuBarViewModel( IMenuBarView view)
{
View = view;
View.ViewModel = this;
}
public IView View
{get; set;}
}
We are not yet done. First, we need to register all our new types to container, inside Module class, as:
public class MyModule : IModule
{
IUnityContainer _container;
public MyModule(IUnityContainer container)
{
_container = container ;
}
public void Initialize()
{
_container.RegisterType<IMenuBarView, MenuBarView>();
}
}
What above snippet is doing is,
whenever I’ll ask for type IMenuBarView
, I’ll get instance of MenuBarView
and
we also have to do the same thing for our IMenuBarViewModel
, as:
public class MyModule : IModule
{
IUnityContainer _container;
public MyModule(IunityContainer container)
{
_container = container ;
}
public void Initialize()
{
_container.RegisterType<IMenuBarView, MenuBarView>();
_container.RegisterType<IMenuBarViewModel, MenuBarViewModel>();
}
}
Now, if you will run your application, you will still get an empty Shell. This is because, we haven’t composed our view yet.
View Composition
View Composition is the process of constructing a view. View consists of many visual elements. As and when these
elements will be created, Views will be displayed in the Regions, created by Shell. These items can be displayed using automatic View Discovery or by using View Injection.
View Discovery
With View Discovery, views are added to Regions automatically. To enable View Discovery, we need to set a relationship
in the Region-View registry between a region’s name and a type of view. To do this, one can simply call the
RegionManager.RegisterViewWithRegion(name, type)
.
Typically this is done, when a Module initializes or when user performs an action. When the Region is created, it
looks for all the view types that are associated with the Region and automatically instantiates those views.
A side-effect of this behaviour is that
we don’t have explicit control of when to load and display a view inside a
Region.
Sample code for View Discovery
Let’s open our MyModule
class and add
the following code:
public class MyModule : IModule
{
IUnityContainer _container;
IRegionManager _manager;
public MyModule(IUnityContainer container, IRegionManager manager)
{
_container = container ;
_manager = manager;
}
public void Initialize()
{
_container.RegisterType<IMenuBarView, MenuBarView>();
_container.RegisterType<IMenuBarViewModel, MenuBarViewModel>();
_manager.RegisterViewWithRegion(RegionNames.MenuBarRegion, typeof(MenuBarView));}
}
Now, whenever the Region is created,
the Region will automatically initialize and instantiate our MenuBar
item. Now
at this point, if you will run this, you will see your MenuBarView
in Shell.
Here, we have very limited control on
how and when MenuBar
is initialize and instantiated.
View Injection
With View Injection, views are added programmatically
to the Region. This can also be done, when a Module initializes or as a result
of user interaction. One can achieve View Injection by couple of different
ways. This can be done by calling RegionManager.Region[“Name”].Add(view, name)
or we can get an instance of an IRegion
from a RegionManager
and then work with
the Region directly as IRegion.Add(view, name)
.
Please keep in mind that while programmatically
adding views, one need to activate/deactivate views. For example, if your
content control has a Region, which already contains a view and if you add a
new view to it, you need to deactivate the current view for the newly injected
view to show.
So, as we can see View Injection gives
us more control over views to be loaded and display. We can also remove views
from the Region.
One thing to keep in mind with View
Injection is that you can’t add a View to Region that has not been created. For
View Discovery, view is automatically created when a Region is created. So, one
should be aware of what all Regions have been created, before you try to inject
a view into it.
Sample code for View Injection
Let’s again open our MyModule
class and
add the following code:
public class MyModule : IModule
{
IUnityContainer _container;
IRegionManager _manager;
public MyModule(IUnityContainer container, IRegionManager manager)
{
_container = container ;
_manager = manager;
}
public void Initialize()
{
_container.RegisterType<IMenuBarView, MenuBarView>();
_container.RegisterType<IMenuBarViewModel, MenuBarViewModel>();
var viewModel = _container.Resolve<IMenuBarViewModel>();
_manager.Regions[RegionNames.MenuBarRegion].Add(viewModel.View);
}
}
Now, run the application, you will be
see your MenuBar
view. Sometimes, we need a more control of the Region and in
that case, we can get instance of the Region using IRegion
as:
public class MyModule : IModule
{
...
...
public void Initialize()
{
...
...
var viewModel = _container.Resolve<IMenuBarViewModel>();
IRegion region = _manager.Regions[RegionNames.MenuBarRegion];
region.Add(viewModel.View);
}
}
Now run the application, and you will get the expected output.
Hope you enjoyed reading this article.
Just watch my next article of this series, which is going to publish soon !!!