Prism - 體會Shell和Module
這篇的內容,主要是翻譯於這個網站的內容,是由國外的Nick Polyak高手所寫的,然後再加上小弟雜多的解釋內容XDD,所以內容不是百分百和原網站一樣,也請各位多多包涵 ( 所以這篇可以算是讀後心得了,不算翻譯文了.. )
最簡單!有xaml的Prism程式
上一篇,示範了完全沒有xmal的程式,利用完全沒有xaml的程式來專注於Bootstrapper
上;而這篇,會示範一個最簡單,有xaml、Shell、Module的Prism程式,如果沒看過前篇,建議看一下會比較好,因為有很多的細節( 例如:Module專案怎麼建、要參考那些dll,有些不要copy local)的設定,不會再這一篇出現了。
什麼叫做最簡單呢!?運作出來的結果如下,我們可以看到畫面非常的單純( 和上一篇比,至少有字了 ),只有兩串字,最上面是整個Shell,而中間紅色的字,則是利用Prism嵌入進來的Module1。
Shell
Shell在Prism裡面,就像一個重劃區土地一樣,負責提供最底層的地;而地裡面會再區分一個又一個的Region,每個Region上面就可以把Module蓋上去,就像是蓋個新光、大遠百之類的建築物。
Shell和Module的XAML
開始前,我們會先把放置Shell的Main Project和放置Module的Module Project準備好;當建立這兩個專案後,預設每個專案一定都會有一個MainPage.xaml,而這邊,我們分別把這兩個專案的MainPage.xaml改名,Main Project的部分,當然是改成Shell.xaml嚕,而Module的部分,就改成Module1View.xaml ( 再次提醒,如果沒看過前篇的人,建議看一下,因為有很多的細節需要處理。 );而下面是先列出Shell和Module的XAML,以下是Shell的部分,我們可以看到,其實沒有多大的變化,只是我們多增加了prism的namespace,並且在ContentControl裡面,定義了MyRegion1的Region Name。
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:prism="http://www.codeplex.com/prism">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="這裡是Shell,不是Module"
FontSize="25"
Foreground="Blue"
HorizontalAlignment="Center"
VerticalAlignment="Top"/>
<!-- 放置Region,並給個名子-->
<ContentControl HorizontalAlignment="Center"
VerticalAlignment="Center"
prism:RegionManager.RegionName="MyRegion1"/>
</Grid>
</UserControl>
接下來是Module的XAML,這部分就很簡單,裡面只放入一個TextBlock,其他沒甚麼改變。
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">
<TextBlock Text="我是Module1"
FontSize="25"
Foreground="Red"
/>
</UserControl>
接下來,我們來看看程式碼的部分。
Shell和Module的程式碼
Shell.cs的部分也很簡單,我們只要在Shell Class上面加上Export這個屬性,讓MEF知道要利用這個做輸出。
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
namespace PrismDemo2HaveVisual
{
[Export]
public partial class Shell : UserControl
{
public Shell()
{
InitializeComponent();
}
}
}
接下來,我們看看Module的部分,這部分也只需要加上Export屬性。
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
namespace Module1
{
[Export(typeof(Module1View))]
public partial class Module1View : UserControl
{
public Module1View()
{
InitializeComponent();
}
}
}
就這樣,就完成了視覺的部分。
建立Bootstapper Class
這個部份我相信大家應該都很有經驗了,大家可以直接看看下面的程式碼,比較特別的是ConfigureAggregateCatalog的方法裡面加了一行程式碼,我下面有很長的註解,如果看不太懂也沒關係,下一篇會針對原始碼做解說,這邊只要記住,是把Shell註冊進MEF就對了;其他部分則沒甚麼改變。
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Prism.MefExtensions;
using System.ComponentModel.Composition.Hosting;
using Microsoft.Practices.Prism.Modularity;
namespace PrismDemo2HaveVisual
{
public class TheBootstrapper : MefBootstrapper
{
protected override void InitializeShell()
{
base.InitializeShell();
//將Shell指定給Application的RootVisual,
//也就是說,此Application的最上層就是Shell了。
Application.Current.RootVisual = (UIElement) Shell;
}
protected override DependencyObject CreateShell()
{
//產生Shell。
return Container.GetExportedValue();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
//這裡很有趣,AggregateCatalog這個變數是一個AggregateCatalog型別( 別懷疑,原始碼就是這樣,大小寫都一樣 )
//而我們這邊要多加一個AssemblyCatalog進去這個Collection裡面,
//而使用new AssemblyCatalog帶入的參數,會自動去搜尋這個Assembly裡面有符合的AssemblyCatalog,
//我們的目的就是希望把Shell加入進去。
//那為何用this呢?,因為這裡面就含有Shell( 也就是Shell.xaml.cs ),
//而這個Shell的程式碼裡面有Export,而這個Export就是這裡MEF需要的。
//但是Shell和這個類別好像沒關聯阿!?
//原因是因為this.GetType().Assembly這個地方,Assembly代表的是組件,
//換言之,他搜尋的層級並非只是這個Class.而是整個NameSpace的層級,
//剛好,好死不死,Shell和這個class的NameSpace是一樣的。
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
}
protected override IModuleCatalog CreateModuleCatalog()
{
//最先進來
ModuleCatalog moduleCatalog = new ModuleCatalog();
//加入模組,這裡只是單純的加入模組,並不會決定放置的位置。
moduleCatalog.AddModule
(
new ModuleInfo
{
InitializationMode = InitializationMode.WhenAvailable,
Ref = "Module1.xap",
ModuleName = "Module1Impl",
ModuleType = "Module1.Module1Impl, Module1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
}
);
return moduleCatalog;
}
}
}
到這邊,Bootstrapper就算完成了。
更改App.xaml.cs
我們改用TheBootstrapper為主要的啟動物件。
{
(new TheBootstrapper()).Run();
}
最後,我們要建立一個實作IModule的物件。
建立實作IModule的物件Module1Impl
同樣的,我們最後必須建立一個實作IModule的物件,當此物件被建構的時候,會去利用MEF來注入一個實作IRegionManager的類別TheRegionManager,而在運作整個Prism的時候,並會利用TheRegionManager來將這個Module注入到Shell裡面的特定位置。
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.MefExtensions.Modularity;
using System.ComponentModel.Composition;
using Microsoft.Practices.Prism.Regions;
namespace Module1
{
[ModuleExport(typeof(Module1Impl))]
public class Module1Impl : IModule
{
[Import]
public IRegionManager TheRegionManager { private get; set; }
#region IModule Members
public void Initialize()
{
//由外面傳進來的RegionManager,RegionManager用來控制此View要與哪個XAML的Region做關聯。
TheRegionManager.RegisterViewWithRegion("MyRegion1", typeof(Module1View));
}
#endregion
}
}
就這樣,常常得撰寫過程後,就完成了,整個專案的文件夾,會如下圖。
後記
如前面所說,Prism的入門比較高,但是好處卻是很大的,藉由Module的拆開,我們就可以很輕鬆地拆開來開發,而不會整個架構弄得亂七八糟,測試等等的流程,也會簡單許多,程式碼也會乾淨許多,但學習曲線是比較高的;最後,寫完這篇,或是看完這篇的人,或許還會有許多疑問,這時候,我們就準備要進入Bootstrapper的原始碼來看看了,下一篇,會介紹比較底層的架構,相信看完,會更加的了解,為什麼要這樣做。