Starting a modular application development using Prism,
WPF and Unity container.
Step 1
·
In Visual Studio 2010, start a new WPF project.
·
Remove MainWindow.xaml file that was
automatically created.
· Remove StartupUri="MainWindow.xaml" from App.xaml file.
· Add the following references
· Remove StartupUri="MainWindow.xaml" from App.xaml file.
· Add the following references
o
Microsoft.Practices.Prism
o Microsoft.Practices.Prism.Unity
o Microsoft.Practices.Prism.UnityExtensions
o Microsoft.Practices.Prism.ServiceLocation
o Microsoft.Practices.Prism.Interactivity
·
Create a class called Bootstrapper.cs and derive
this from UnityBootstrapper class.
·
Create
the main shell window by adding a new wpf window called PrismAppShell.xaml
·
Implement the abstract method CreateShell in
Bootstrapper class.
·
Override two methods in Bootstrapper class
o
InitializeModules
o
ConfigureModuleCatalog
·
Inside App.xaml.cs within OnStartup method instantiate Bootstratper object and call its Run()
Inside App.xaml.cs within OnStartup method instantiate Bootstratper object and call its Run()
Therefore the files are
going to look like this
App.xaml
<Application x:Class="Prism.ModuleExmaple.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs
public partial class App : Application
{
protected
override void
OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
BootStrapper
bootstrapper = new BootStrapper();
bootstrapper.Run();
}
}
Bootstrapper.cs
protected
override System.Windows.DependencyObject CreateShell()
{
return
this.Container.Resolve<PrismAppShell>();
}
protected
override void
InitializeModules()
{
base.InitializeModules();
App.Current.MainWindow
= (PrismAppShell)this.Shell;
App.Current.MainWindow.Show();
}
protected
override void
ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
this.ModuleCatalog.AddModule(null); // TODO -
placeholder
}
}
PrismAppShell.xaml
<Window x:Class="Prism.ModuleExmaple.PrismAppShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PrismAppShell" Height="300" Width="300">
<Grid>
</Grid>
</Window>
Now one with run this application from visual studio the
empty shell window will come up like below.
Step 2
Now let us divide the main shell xaml into regions and mark them with regional names.
Step 3
In step 3. let us create few individual modules such and load them on the regions on the shell as we defined before. Also in the shell xaml on the top we used a ribbon control and defined the RibbonRegion there, a TreeView control on the left for TreeRegion, a TabControl in the middle for BlotterRegion. Bottom and right (StatusRegion and AlertRegion) we kept as ContentControl still.
Step 2
Now let us divide the main shell xaml into regions and mark them with regional names.
<Window x:Class="Prism.ModuleExmaple.PrismAppShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://www.codeplex.com/prism"
Title="PrismAppShell" Height="900" Width="1200">
<Grid x:Name="LayoutRoot">
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch" Name="dockPanel1" VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Background="#FFCCD4F8" Height="100">
<ContentControl prism:RegionManager.RegionName="RibbonRegion"></ContentControl>
</StackPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Background="#FFD9E1EF" Height="100">
<ContentControl prism:RegionManager.RegionName="StatusRegion"></ContentControl>
</StackPanel>
<ScrollViewer>
<StackPanel Orientation="Vertical" DockPanel.Dock="Left" Background="#FF50576F" Width="200">
<ContentControl prism:RegionManager.RegionName="TreeRegion"></ContentControl>
</StackPanel>
</ScrollViewer>
<StackPanel Orientation="Vertical" DockPanel.Dock="Right" Background="#FF677BA7" Width="100">
<ContentControl prism:RegionManager.RegionName="AlertRegion"></ContentControl>
</StackPanel>
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFC0DBF2">
<ContentControl prism:RegionManager.RegionName="BlotterRegion" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentControl>
</StackPanel>
</ScrollViewer>
</DockPanel>
</Grid>
</Window>
Step 3
In step 3. let us create few individual modules such and load them on the regions on the shell as we defined before. Also in the shell xaml on the top we used a ribbon control and defined the RibbonRegion there, a TreeView control on the left for TreeRegion, a TabControl in the middle for BlotterRegion. Bottom and right (StatusRegion and AlertRegion) we kept as ContentControl still.
<Window x:Class="PrismApp.Shell.PrismAppShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://www.codeplex.com/prism"
Title="PrismAppShell" Height="900" Width="1200"
xmlns:my="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
>
<Window.Resources>
<DataTemplate x:Key="crap">
<StackPanel Width="100" Height="18"></StackPanel>
</DataTemplate>
<Style x:Key="TabItemStyleKey" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Path=DataContext.ViewTile}"></Setter>
<Setter Property="HeaderTemplate" Value="{Binding Source={StaticResource crap}}"></Setter>
</Style>
<Style x:Key="TreeItemStyleKey" TargetType="{x:Type TreeViewItem}">
<Setter Property="Height" Value="Auto"></Setter>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="BorderThickness" Value="0" />
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch" Name="dockPanel1"
VerticalAlignment="Stretch">
<my:Ribbon prism:RegionManager.RegionName="RibbonRegion" DockPanel.Dock="Top"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="ribbon1" Height="150"/>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Background="#FFD9E1EF" Height="100">
<ContentControl prism:RegionManager.RegionName="StatusRegion"></ContentControl>
</StackPanel>
<ScrollViewer>
<StackPanel Orientation="Vertical" DockPanel.Dock="Left" MinWidth="150" MaxWidth="200">
<TreeView ItemContainerStyle="{StaticResource TreeItemStyleKey}"
prism:RegionManager.RegionName="TreeRegion"
Name="treeView1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="0"
/>
</StackPanel>
</ScrollViewer>
<StackPanel Orientation="Vertical" DockPanel.Dock="Right" Background="#FF677BA7" Width="100">
<ContentControl prism:RegionManager.RegionName="AlertRegion"></ContentControl>
</StackPanel>
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFC0DBF2">
<TabControl ItemContainerStyle="{StaticResource TabItemStyleKey}"
prism:RegionManager.RegionName="BlotterRegion"
Name="tabControl1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Width="Auto">
</TabControl>
</StackPanel>
</ScrollViewer>
</DockPanel>
</Grid>
</Window>
Cash blotter module registers view like this.
public class CashBlotterModule
: IModule
{
private
readonly IRegionViewRegistry
regionViewRegistry = null;
public
CashBlotterModule(IRegionViewRegistry
regionViewRegistry)
{
this.regionViewRegistry
= regionViewRegistry;
}
public void Initialize()
{
this.regionViewRegistry.RegisterViewWithRegion("BlotterRegion", typeof(CashBlotterView));
this.regionViewRegistry.RegisterViewWithRegion("TreeRegion", typeof(CashItemsTreeView));
}
}
And bootstrapper loads module like this. (IN next section
this will be worked on further to load using container and decouple.
public class BootStrapper
: UnityBootstrapper
{
protected
override System.Windows.DependencyObject CreateShell()
{
return
this.Container.Resolve<PrismAppShell>();
}
protected
override void
InitializeModules()
{
base.InitializeModules();
App.Current.MainWindow
= (PrismAppShell)this.Shell;
App.Current.MainWindow.Show();
}
protected
override void
ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
ModuleCatalog
moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
moduleCatalog.AddModule(typeof(PrismApp.Module.Cash.Blotter.CashBlotterModule)); //
Seems like as soon as you add a new module, internally seletor module adopter
(since blotter region is on tab control), automatically adds a tab item)
moduleCatalog.AddModule(typeof(PrismApp.Module.Deriv.Blotter.DerivBlotterModule));
//moduleCatalog.AddModule("CashBlotterModule",
"PrismApp.Module.Cash.Blotter.CashBlotterModule");
}
}
In above code, inside ConfigureModuleCatalog the module
loading will be upgraded in the following section.
When we run now, the skeleton application comes up like this
Step 4
In step 4, we have
After all the above steps in step 4, our skeleton app looks like the following.
When we run now, the skeleton application comes up like this
Step 4
In step 4, we have
- Replaced the wpf datagrid with devexpress grid control, wpf toolbox ribbon with devexpress ribbon control.
- View Injection - done view injection from cash blotter module and ticket module and as well from derivatives blotter and ticket module.
- Injected views in desired prism regions on the shell, i.e. on BlotterRegion, RibbonRegion, TreeRegions.
- Model - connected to database for fake trade data.
- ViewModel - created a number of view model classes in order for the views to bind to trades data.
After all the above steps in step 4, our skeleton app looks like the following.
How far have you gone in this implementation? what mechanism you are using to push values to all the connected blotters from the server
ReplyDeleteHi Milind,
ReplyDeleteYou should be able to have full fledge production quality applications based on the above structure. However for this blog I just did with play data.
For a real system, you can use any number of data sources, it does not matter. You can use Rx (Reactive Extension) if you desire push based data, or in memory chached data (with or without commercial in-memory product) and then register event handlers from blotter-view-models to get updates for blotter. View models can listen to message queues, or retrive data from databases or have LINQ querries, object data sources, anything you would like.
For simple straight forward way, you can directly get from database (for this play application that is what I am doing, I am getting dummy data from my local sql server instance).
Thanks, Can you please share the sample code for this app?
DeleteI was hoping that your blog article would be a good intro to Prism, but in VS2012 and Prism 4.1 I am getting a syntax error on your CreateShell method. The return line underscores the Resolve() method and states that it cannot be used with type arguments. Any ideas? I will continue on with the project for background purposes but would really like to run the program.
ReplyDeleteUse TryResolve()
DeleteHi Gerald, could you please upload the source code? I am new to PRISM....
ReplyDeleteThanks!
I'm fairly knowledgeable with WPF and MVVM. I followed the above steps (up to step 3) but the solution won't compile. Can you provide the source code?
ReplyDeleteHi,
ReplyDeleteWhat about all the code source please ? Could you send it to me all the source code please ?
Thanks, Can you please share the sample code for this app?
ReplyDeleteGreat article. I am interested in module navigation and dynamically ribbon population based on modules loaded. Any implementation hints?
ReplyDeleteThanks!
How do you connect your viewmodel to view in this scenario. I have a similar structured project where I have a main project with a xaml file that contains contentcontrol region and another project as a module where I define a UserControl. I load my modules using a app.config file rather that coding in Bootstrapper. I am able to successfuly display usercontrol in the contentcontrol using RegionManager.RegisterViewToRegion(...) and I do this in Initialize method in a class that implements IModule interface. No the trouble I have is how do i tie or register this user control to a viewmodel that is in same module as the user control. I tried few different ways but unsuccessful. I am using PRISM 6.3. Any thoughts or suggesstion is greatly appreciated.
ReplyDeleteNicely explained article by the way...
ReplyDelete