Wednesday 31 October 2012

Prism Modularity using Unity Container


In Prism we have 2 different dependency Injection Containers: Unity and MEF

Here I ll demonstrate Unity approach for creating the Modular Silverlight Application.

Let’s say we have the following modules –

Header Module – HMModule
Left Module  – LMModule
Center Module  – CMModule
OnDemand Module – DMModule

So the look will be something like below -

Here I am trying to achive the following –

1.      The first 3 module should be downloaded and Initialized on Application Start.
2.      The respective View should be registed to the specific Region of the Shell.
3.      Click on a View of the Hearder to be Handled by the Center Module – Ex of Event Aggregator
4.      Click on the Button in Center Module should dynamicy Load the Module and the specific View should be displayed in the RadWindow.
5.      How to use ServiceLocator to get the instance of Prism interfaces.

Code - Shell.xaml
<UserControl x:Class="PrismStart.Shell"
    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:R="http://www.codeplex.com/prism"
    mc:Ignorable="d"
    d:DesignHeight="600" d:DesignWidth="900">  
    <Canvas>
        <ItemsControl Canvas.Left="0" Canvas.Top="0" x:Name="HMRegion" R:RegionManager.RegionName ="HMRegion" Height="85" Width="888" />
        <ItemsControl Canvas.Left="0" Canvas.Top="90" x:Name="LMRegion" R:RegionManager.RegionName ="LMRegion" Height="468" Width="100"/>
        <ItemsControl Canvas.Left="110" Canvas.Top="90"  x:Name="CMRegion" R:RegionManager.RegionName ="CMRegion" Height="468" Width="778"/>
    </Canvas>
</UserControl>

I am creating 3 region in the Shell as highlighted above

Code -  Bootstrpper.cs
using System.Windows;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Prism.Modularity;
namespace PrismStart
{
    public class Bootstraper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            Shell shell = new Shell();
            Application.Current.RootVisual = shell;
            return shell;
        }
        protected override IModuleCatalog CreateModuleCatalog()
        {
            ModuleCatalog mc = new ModuleCatalog();
            ModuleInfo mi1 = new ModuleInfo()
            {
                ModuleName = "Header",
                InitializationMode = InitializationMode.WhenAvailable,
                ModuleType = "HMModule.HM, HMModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                Ref = "HMModule.xap"
            };
            ModuleInfo mi2 = new ModuleInfo()
            {
                ModuleName = "Centre",
                InitializationMode = InitializationMode.WhenAvailable,
                ModuleType = "CMModule.CM, CMModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                Ref = "CMModule.xap"
            };
            ModuleInfo mi3 = new ModuleInfo()
            {
                ModuleName = "Left",
                InitializationMode = InitializationMode.WhenAvailable,
                ModuleType = "LMModule.LM, LMModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                Ref = "LMModule.xap"
            };
            ModuleInfo mi4 = new ModuleInfo()
            {
                ModuleName = "OnDemand",
                InitializationMode = InitializationMode.OnDemand,
                ModuleType = "OnDemandModule.OM, OnDemandModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                Ref = "OnDemandModule.xap"
            };
            mc.AddModule(mi1); mc.AddModule(mi2); mc.AddModule(mi3); mc.AddModule(mi4);
            return mc;
        }
    }
}

Here you can notice how are creating Module Catalog with the InitializationMode to be either WhenAvailable or OnDemand. The same can be achieved using the ModuleCatalog.xaml file. The content of the same will be similar to this.

ModuleCatalog.xaml
<prism:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:sys="clr-namespace:System;assembly=mscorlib"
               xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<prism:ModuleInfo Ref="HMModule.xap" ModuleName="Header" ModuleType="HMModule.HM, HMModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" InitializationMode="WhenAvailable"/>
<prism:ModuleInfo Ref="CMModule.xap" ModuleName="Centre" ModuleType="CMModule.CM, CMModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" InitializationMode="WhenAvailable"/>
  <prism:ModuleInfo Ref="LMModule.xap" ModuleName="Left" ModuleType="LMModule.LM, LMModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" InitializationMode="WhenAvailable"/>
  <prism:ModuleInfo Ref="Sunix.Acc.App.xap" ModuleName="Accounting" ModuleType="Sunix.Acc.App.AccModule, Sunix.Acc.App, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" InitializationMode="OnDemand"/>
</prism:ModuleCatalog>

Next you need to modify the CreateModuleCatalog method as below –

protected override IModuleCatalog CreateModuleCatalog()
{
       ModuleCatalog moduleCatalog = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("ModuleCatalog.xaml",   UriKind.Relative));
            return moduleCatalog;
 }


Code – Header Module (HM.cs)
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;

namespace HMModule
{
    public class HM : IModule
    {
        IRegionManager rm;
   
        public HM(IRegionManager _rm)
        {
            rm = _rm;
        }

        public void Initialize()
        {
            rm.RegisterViewWithRegion("HMRegion", typeof(HMView));
        }
    }
}


Here you can see how I am registering a View to the Prism Region.

Code – HMView (xaml and cs)
<UserControl x:Class="HMModule.HMView"
    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:DesignHeight="100" d:DesignWidth="800">

  <Grid x:Name="LayoutRoot" Background="#FF2B2525">
    <StackPanel Orientation="Vertical">
      <TextBlock Text="Header Module" Foreground="#FFDEC2C2" Height="50" FontSize="14" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
      <StackPanel Orientation="Horizontal">
        <Button Command="{Binding headerClicked,Mode=TwoWay}" CommandParameter="{Binding ElementName=textBox1,Path=Text,Mode=TwoWay}" Content="Publish Event - To be Handled by Center Module" Height="23" Name="button1" VerticalAlignment="Top" />
                <TextBlock Text="                    Parameter Value to be Passed :" Foreground="#FFE2CDCD" />
                <TextBox Height="23" HorizontalAlignment="Left"  Name="textBox1" VerticalAlignment="Top" Width="150" />
      </StackPanel>
    </StackPanel>
  </Grid>
</UserControl>


using System.Windows.Controls;

namespace HMModule
{
    public partial class HMView : UserControl
    {
        public HMView(HMViewModel hMViewModel)
        {
            InitializeComponent();
            this.Loaded += (s, e) => { this.DataContext = hMViewModel; };
        }
    }
}

Code – HMViewModel.cs
using System.Windows.Input;
using Microsoft.Practices.Prism.Events;
using SahreCode;
using SharedCode;

namespace HMModule
{
    public class HMViewModel
    {
        IEventAggregator aggregator { get; set; }
        public ICommand headerClicked { get; set; }
       
        public HMViewModel(IEventAggregator _aggregator)
        {
            headerClicked = new DC(executeCentre);
            aggregator = _aggregator;           
        }

        public void executeCentre(object param)
        {
            aggregator.GetEvent<HeaderViewEvent>().Publish(param.ToString());     
        }
    }
}

Code – Center Module (CM.cs)
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;

namespace CMModule
{
    public class CM : IModule
    {
        IRegionManager rm;
        IModuleManager mm;
        public CM(IRegionManager _rm, IModuleManager _mm)
        {
            rm = _rm;
            mm = _mm;
        }

        public void Initialize()
        {
            rm.RegisterViewWithRegion("CMRegion", typeof(CMView));
        }
    }
}

Code – CMView (xaml and cs)
<UserControl x:Class="CMModule.CMView"
    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"
    Height="600" Width="800">
   
    <Grid x:Name="LayoutRoot" Background="#FFC7C2C2">
        <Grid.RowDefinitions>
            <RowDefinition Height="42*" />
            <RowDefinition Height="55*" />
            <RowDefinition Height="503*" />
        </Grid.RowDefinitions>
        <TextBlock Text="Center Module" FontSize="15" FontWeight="Bold" Foreground="Black" Grid.RowSpan="2" />
        <TextBlock Grid.Row="1" Text="This module View is subscribing to the Event Raised/Published by the Header Module View" FontSize="11" FontWeight="Bold" Foreground="Black" Grid.RowSpan="2" />
        <Button Grid.Row="2" Content="Open View from OnDemandLoad" Height="23" HorizontalAlignment="Left" Name="Demand" VerticalAlignment="Top" Width="221"  Command="{Binding DemandCommand,Mode=TwoWay}"  />
    </Grid>
</UserControl>



using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Unity;

namespace CMModule
{
    public partial class CMView : UserControl
    {
        private CMViewModel cMViewModel;
        private IModuleManager mm;
        private IUnityContainer container;
        public CMView(CMViewModel cMViewModel, IModuleManager _mm, IUnityContainer _container)
        {
            InitializeComponent();
            mm = _mm;
            container = _container;
            this.cMViewModel = cMViewModel;
            this.Loaded += new RoutedEventHandler(CMView_Loaded);
        }

        void CMView_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = cMViewModel;
           
        }
    }
}

Code – CMViewModel.cs
using System;
using System.Windows.Input;
using SharedCode;
using Microsoft.Practices.Prism.Events;
using SahreCode;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Unity;
using Telerik.Windows.Controls;
using System.Linq;
using System.Collections.Generic;

namespace CMModule
{
    public class CMViewModel
    {
        private IEventAggregator aggregator;
        private IModuleManager mm;
        private IUnityContainer container;
        public int MyProperty { get; set; }
        public ICommand DemandCommand { get; set; }
        private List<string> DownloadedModuleList;
        private string CurrentModuleName;
        
        public CMViewModel(IEventAggregator _aggregator, IModuleManager _mm, IUnityContainer _container)
        {
            aggregator = _aggregator;
            mm = _mm;
            container = _container;
            DemandCommand = new DC(OnDemand);
            aggregator.GetEvent<HeaderViewEvent>().Subscribe(CentreHandler);
            DownloadedModuleList = new List<string>();
        }

        private void OnDemand(object param)
        {
            CurrentModuleName = "OnDemand";
            if (!DownloadedModuleList.Contains(CurrentModuleName))
            {
                DownloadedModuleList.Add(CurrentModuleName);
                mm.LoadModuleCompleted += new EventHandler<LoadModuleCompletedEventArgs>(mm_LoadModuleCompleted);
                mm.LoadModule(CurrentModuleName);
            }
            else
                ShowView();
        }

        void mm_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e)
        {
            ShowView();
        }
        private void ShowView()
        {
            Type x = container.Registrations.Where(o => o.Name == "OMView").Select(y => y.RegisteredType).FirstOrDefault();
            var g = container.Resolve(x, "OMView");
            RadWindow win = new RadWindow();
            win.Content = g;
            win.Show();
        }
        public void CentreHandler(string mess)
        {
            System.Windows.MessageBox.Show("Parameter Passed: " + mess);
        }
    }
}

Above highlighted piece of code is demonstarting how the OnDemand Module load is working.

Code – OnDemand Module (OM.cs)
using Microsoft.Practices.Unity;
using Microsoft.Practices.Prism.Modularity;

namespace OnDemandModule
{
    public class OM : IModule
    {      
        private IUnityContainer container;
        public OM(IUnityContainer _container)
        {
            container = _container
            container.RegisterType(typeof(OMView), "OMView", new TransientLifetimeManager(), new Microsoft.Practices.Unity.InjectionConstructor("DHANANJAY"));
        }
        public void Initialize()
        {           

        }
    }
}

<UserControl x:Class="OnDemandModule.OMView"
    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:DesignHeight="300" d:DesignWidth="400">
   
    <Grid x:Name="LayoutRoot" Background="White">
      <StackPanel Orientation="Vertical">
      <TextBlock Text="I am On Demand View"/>
      <TextBlock Text="{Binding  PatientName}"/>
        <Button Content="Change Patient" Command="{Binding ChangePatient}"/>
      </StackPanel>
    </Grid>
</UserControl>

using System.Windows.Controls;

namespace OnDemandModule
{
    public partial class OMView : UserControl
    {
        public OMView(string ctr)
        {
            InitializeComponent();
            this.DataContext = new OMViewModel();
        }
    }
}


using System.Windows.Input;
using SahreCode;
using Microsoft.Practices.Unity;

namespace OnDemandModule
{
    public class OMViewModel : ViewModelBase
    {
        private string patientName;
        private IUnityContainer _container;
        public string PatientName
        {
            get { return patientName; }
            set { patientName = value; firePC("PatientName"); }
        }
        public ICommand ChangePatient { get; set; }

        public OMViewModel()
        {
            ChangePatient = new DC(OnChangePatient);
            PatientName = "Dhananjay";
            IUnityContainer container = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IUnityContainer>();
        }
        public void InitializeVM(IUnityContainer container)
        {
            _container = container;
        }
        private void OnChangePatient(object p)
        {
            PatientName = "Manav";
        }
    }
}

private void Application_Startup(object sender, StartupEventArgs e)
        {
            Bootstraper bs = new Bootstraper();
            bs.Run();
        }


Here you can how easily we can make use of ServiceLocator to get the Instances of the Prism core Interfaces.

No comments:

Post a Comment