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()
{ // TO DO: }
}
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 aRegionManager
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 !!!
Comments
Post a Comment