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。
<UserControl x:Class="PrismDemo2HaveVisual.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: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,其他沒甚麼改變。
<UserControl x:Class="Module1.Module1View" 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; 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; 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; 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為主要的啟動物件。
private void Application_Startup(object sender, StartupEventArgs e) { (new TheBootstrapper()).Run(); }
最後,我們要建立一個實作IModule的物件。
建立實作IModule的物件Module1Impl
同樣的,我們最後必須建立一個實作IModule的物件,當此物件被建構的時候,會去利用MEF來注入一個實作IRegionManager的類別TheRegionManager,而在運作整個Prism的時候,並會利用TheRegionManager來將這個Module注入到Shell裡面的特定位置。
using System; 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的原始碼來看看了,下一篇,會介紹比較底層的架構,相信看完,會更加的了解,為什麼要這樣做。
參考資料
- [http://msdn.microsoft.com/en-us/library/ff921153%28v=pandp.40%29.aspx](http://msdn.microsoft.com/en-us/library/ff921153%28v=pandp.40%29.aspx)