Prism學習筆記


最近3周忙於學習Prism,留下些學習筆記.....最終發現,還是必須得,親自仔細閱讀官方指導,一種不錯的學習方式。

 

要點1:

        在CAL中,模塊是實現Imodule接口的類,此接口僅包含一個方法,稱為Initializae。如果把引導程序看做應用程序的Main方法,那么Initialize方法就是每個模塊的Main方法。

要點2:

       對於Module中的Initialize方法中,提到的_contianer 和 _regionManager的作用需討論一下。如果接口並未定義他們,那么他們從何而來?我們是否需要將邏輯硬編碼到模塊中以找出這些依賴關系?幸運的是,后一個問題的答案是“否”,此時,IoC容器就派上用場。加載此模塊時,它從容器中被解析出來,同時會將所有指定的依賴關系注入到模塊的構造容器中。

       通過讓模塊直接訪問容器,可以允許模塊以一種強制性的方式在容器中注冊和解析依賴關系。

要點2 :

      CAL(Compoise Application Library)包含一個Region類,大體上,此類就是涵蓋這些位置的一個句柄。Region類包含一個Views屬性,它主要是在區域中進行顯示的視圖的只讀集合。視圖通過調用區域的add方法被添加到區域中。Views屬性實際包含對象的泛型集合;它並非局限於僅包含UIElement。此集合將實現InotifyPropertyCollectionChanged,以使同區域相關聯的UIElement能夠與之綁定並觀察其變更。

要點3:
      為什么Region類的Views集合為弱類型而非UIElement類型。由於WPF可提供豐富的模板支持,因此您可以將模型直接添加到區域。然后,將會為該模型定義一個相關聯的DataTemplate,它會定義模型的呈現方式。如果添加的項目是UIElement或者用戶控件,者WPF會按原樣呈現。這意味着為某個區域添加控件,只需添加相應的Model,然后定義一個自定義的DataTemplate即可控制顯示,而不是創建一個自定義的OderView控件。

要點4:區域的兩種注冊方式:

        第一種是在XAML中通過使用附加屬性的RegionName 來注釋UIElement進行定義。如:定義MainToolbarRegion的XAML如下:

<ItemsControl Grid.Row="1" Grid.Column="1"

  x:Name="MainToolbar"

  cal:RegionManager.RegionName="MainToolbarRegion">

  <ItemsControl.ItemsPanel>

      <ItemsPanelTemplate>

        <WrapPanel />

    </ItemsPanelTemplate>

  </ItemsControl.ItemsPanel>

</ItemsControl>

       通過XAML定義區域以后,他會在運行時自動注冊RegionManager,這是由引導程序注冊的一個符合服務。RegionManager實質是一個Dictionary,其中關鍵字為區域的名字,值是Iregion接口的實例。需要注意的是:如果使用附加屬性不起作用,或者需要動態注冊其他區域,您可以手動創建Region類或者派生類的實例,並將其添加到RegionManager的Regions的集合中。

       第二種方法是:CreateRegion,它會返回新的AllActiveRegion實例。區域可包含處於活動狀態或者非活動狀態的視圖。對於ItemsControl,他的所有項目始終都處於活動狀態,因為它沒有選擇意識。但是對於其他類型的區域(如Selector),一次僅選擇一項。視圖可實現IactiveAware接口,以便能夠從其他區域處得到也被選中的通知。只要視圖被選中,他就會將其IsSelected屬性設為true。

要點5:

      適配器本身的實現方式完全取決於它所應用到的UIElement的類型。

要點 6:

      在復合應用程序開發過程中,可能不得不創建一些附加的區域適配器和區域。例如需要用來適應第三方供應商提供的控件的適配器。要注冊新的區域適配器,就需要覆蓋引導程序中的ConfingureRegionAdaterMappins方法。

要點7:本地作用區域

      默認情況下,應用程序中只有一個RegionManager實例,因而使得每個區域的作用范圍都是全局有效,這使用與很多情況。當有時可能希望每個視圖都像外殼一樣擁有自己的區域。CAL允許您為視圖定義本地RegionManager,這樣在其中或者其子視圖中定義的任何區域都會在該本地區域中自動注冊。

要點 8 :

       WPF中的元素可綁定到命令以處理執行邏輯以及啟用或者禁用元素。當UIElement被綁定到命令時,如果命令的CanExecute屬性為false,則它會自動被禁用。在XAML中可通過聲明的方式綁定命令,WPF預設即提供RoutedUICommand。

要點 9 :

       CAL包括許多新命令(如DelegateCommand<T>,它允許您為構造函數中的Execute和CanExecute方法制定兩個委派)。通過此命令,在連接各個視圖時,您不必通過在視圖本身定義的方法進行委派,也不必為每個操作分別創建自己的命令。

要點 10 :

      CAL包括CompositeWPFEvent<Tpayload>類,它繼承了EventBase類,並提供對WPF的特定支持。此類使用委派而非網站的.NET事件類執行發布。實質上,它使用默認起委派作用的DelegateReference類(有關弱委派的詳細信息,請參閱 msdn.microsoft.com/library/ms404247)。這將允許對訂閱者進行垃圾回收,及時他們沒有顯示取消訂閱。

 

 

Silverlight about PRISM

要點1 :

       MoudleInfo中指定一個Ref值,Ref值時包含模塊的.xap文件的路徑,在指定.xap文件時,引導程序知道程序集不可用,因此轉到服務器,異步檢索.xap文件。加載.xap文件后,Prism會加載程序集,創建模塊類型並初始化模塊。

對於包含多個模塊的.xap文件,可以創建一個MoudleGroup,用於包含一組MoudleInfo對象,還可以設置MoudleGroup的Ref,以便從單個.xap文件加載所以的模塊。

       在創建.xap文件要包含的Silverlight模塊時,需要創建一個Silverlight應用程序(而不是Silverlight庫)。然后,需要引用要放在.xap文件中的所有模塊項目。您需要刪除app.xaml文件和page.xaml文件,因為該.xap文件不會進行加載,也不會像典型.xap文件那樣運行。該.xap文件只是一個容器(可以是.zip文件)。

要點2:

      CAL支持在XAML中直接定義區域,方法是對RegionManager類使用附加屬性,通過此屬性可以再Shell中指定區域,然后指示區域中應承載的視圖。屬性RegionName可以應用於ItemsControl及其派生控件(如ListBox)、Selector及其派生控件(如TabControl)、以及ContentControl及其派生控件(如ScrollViewer控件)。

要點3:

      如果要使用MVVM模式定義視圖,可以將容器的區域與服務位置相互混合,是視圖和視圖模型相互獨立,從而模塊可在運行時加入視圖和視圖模型。如:如果更改GameListMoudle,則可以向容器注冊視圖和視圖模型,然后再將視圖應用於區域之前加入視圖和視圖模型。如下說是:

public class GameListModule : IModule

{

     IRegionManager regionManager = null;

     IUnityContainer container = null;

     public GameListModule(IUnityContainer con, IRegionManager mgr)

     {

      regionManager = mgr;

      container = con;

   }

     public void Initialize()

     {

          RegisterServices();

         // Build the View

        var view = container.Resolve<IGameListView>();

        // Get an Implemenation of IViewModel

      var viewModel = container.Resolve<IGameListViewModel>();

    // Marry Them
  
    view.ApplyModel(viewModel);

    // Show it in the region

    regionManager.AddToRegion("LookupRegion", view);

  }

  void RegisterServices()

  {

     container.RegisterType<IGameListView, GameListView>();

     container.RegisterType<IGameListViewModel, GameListViewModel>();

  }

}

     通過此方法可以使用UI復合,同時保持MVVM的嚴格分離。

要點4:

      通過UI復合在應用程序中使用多個視圖后,將面臨一個常見問題。即使已構建了獨立視圖來支持更好的測試和開放,通常仍然存在一些接觸點,使得視圖無法完全隔離。因為需要進行通信,這些接觸點在邏輯上是耦合的,不管這些接觸點在邏輯上如何耦合,您還是希望視圖的耦合盡可能松散。

      為實現松耦合和跨視圖通信,CAL支持一種稱為“事件聚合”的服務,通過該事件聚合,全局事件的發布者和使用者可以訪問代碼的不同部分。這類訪問提供了一種非緊密耦合的直接通信方式,可使用CAL的IeventAggregator接口完成,使用該事件可以跨應用程序的不同模塊發布和訂閱事件。

      在通信之前需要,創建一個從CompositePresentationEvent<T>類派生的簡單事件,使用此泛型可以指定要發布的事件的負載。定義完事件后,事件聚合可以通過調用其GetEvent方法來發布事件。這會檢索要聚合的單一事件,調用此方法的第一個事件聚合器和創建單一實例,可以從該事件調用Publish方法創建事件。作為訂閱者,還可以指定在特定情況下調用的篩選器。

要點5:

      在Silverlight中(與WPF不同),沒有真正的命令基礎結構,CAL支持一個類來幫助解決這一問題:DelegateCommand(在Silverlight沒有支持此功能之前)。如要將此命令綁定到XAML中,請使用Microsoft.Practices.Composite.Presentation.Commands命名空間中的Click.Command附加屬性。然后將此命令的值綁定到ViewMode中的命令。

要點 6 :

      依賴關系可以在對象的構造的過程中注入到項目中,這也是依賴關系注入的工作原理。容器的任務就是處理類型的創建,通過容器,可以注冊類型和解析所注冊的類型。

      例如:假定現在有一個IdataAcess接口的具體類型,在啟動應用程序的過程中,可以指示容器注冊該類型,在應用程序中需要該類型的其他任何位置,都可以要求容器解析該類型。

      此外,容器還可以處理依賴關系的結構注入,如果某個需要由容器構造函數創建的對象接受容器可以解析的接口,則容器會解析該類型並將其傳遞給構造函數。

要點 7 :

      向容器注冊類型時,同時指示容器以特殊方式處理該類型的生存期。例如:如果要處理日記記錄組件,則可能希望將該組件視為單一組件,這樣需要日志記錄的每個應用程序部件不會各種獲得一個副本(這是默認行為)。為此,可以提供LifeTimeManager抽象類的實現。

支持多個生存期管理器:

      ContainerControlledLifetimeManager 是按進程的單一實例,而 PerThreadLifetimeManager 是按線程的單一實例。對於 ExternallyControlledLifetimeManager,容器具有對該單一實例的弱引用。如果對象在外部發布,則容器會創建一個新實例,否則返回弱引用中包含的活動對象。

要點 8 :Prism的Command

      P&P 小組也看到了WPF Command 的諸多限制,特別是與UI 元素耦合以及不支持命令組合,所以他們在Prism 中便增加了另外一套Command :DelegateCommand 和CompositeCommand 。

      DelegateCommand :實現了WPF 的ICommand 接口,仍只支持一個CanExecute 和Execute 掛接,但其實現一個稱為IActiveAware 的接口用於指示是否處於集合狀態,非激活狀態的DelegateCommand 始終得不到執行。

CompositeCommand :也是WPF 的ICommand 接口的一個實現,但其同時也是DelegateCommand 的組合,可以向其中注冊或取消注冊DelegateCommand ,當其中所有處於激活狀態的內置DelegateCommand 都可以被執行時其CanExecute 才返回true 。當CompositeCommand 被執行時其內部的DelegateCommand 將被依次執行(如果可執行的話)

      DelegateCommand 和CompositeCommand 的語法層面的東西這里就略過不講了。

      另外,DelegateCommand 和CompositeCommand 並不是RouteCommand 的替代品,而是強有力的補充,RouteCommand 的路由機制讓我們不必關心目前用戶焦點在哪里,況且,WPF 內置的數十種常用Command 為我們節約了不少開發時間。

 

以下6點問答轉自http://msdn.microsoft.com/zh-cn/subscriptions/dd365973.aspx

1. 為什么要使用依賴注入容器

       我們知道, 在Composite Application 中各個模塊之間是松耦合的關系, 也就是在設計的時候盡可能地減少模塊間的依賴, 但無論如何從業務角度講, 他們之間總還是要相互通信與合作的。 所以依賴注入容器在這其中扮演了一個橋梁般的角色。比如當創建某一個組件實例的時候, 其依賴於另外的一個組件或服務, 此時依賴注入容器會將其需要的信息" 注入(Injection)" 進去, 采用注入的方式就避免了直接引用之間的耦合. 除此之外, 使用依賴注入容器還有以下幾方面的好處:

      組件(Component) 不必自己去定位其依賴項和維護依賴項的生命周期,替換組件的依賴項的具體實現時不會影響到組件代碼,由於依賴項比較容易Mock, 所以組件變得更易測。

      由於系統所需要的新的服務很容易被添加到容器中, 系統維護難度也降低了。

2. Prism 的依賴注入容器

      打開Composite Application Library(CAL) 的源代碼, 我們似乎沒有找到一個依賴注入容器的實現, 而是找到了一個依賴注入容器外觀接口IContainerFacade, 其利用了外觀模式來高層抽象了一個依賴注入容器的最最基本的功能" 解析"(Resolve).

事實上Patterns&Practices 團隊在設計時為了讓Prism 兼容更多的依賴注入容器( 比如Spring.Net, Castle Windsor, Unity 等等) 而設計了這一接口, 所以在Prism 源代碼中用到的都是IContainerFacade 接口而不是一個具體的容器.

但是, 在默認情況下,Prism 使用了另外一個開源項目" Unity" 中的輕量級的可擴展的依賴注入容器來作為默認容器UnityContainer. 該容器實現了構造函數注入、屬性注入和方法注入三種注入方式。

3. 依賴注入容器的性能開銷

      由於將類型注冊到依賴注入容器, 然后使用容器來解析(Resolve) 出類型實例對象時, 根本機制是" 反射"(Reflection), 所以開銷是比較大的, 那么如果你需要頻繁地創建大量的實例的話, 是否使用依賴注入容器是需要認真考慮的問題. 大量的依賴和很深的依賴嵌套也會導致性能問題. 另外, 如果一個組件既沒有依賴其它組件或服務, 也不為其他組件所依賴, 那么將其放到依賴注入容器中也是不明智的.

4. 是否注冊為單例

      我們在向依賴注入容器注冊默認類型映射或注冊Instance 時涉及到一個問題是: 是否按照單例模式注冊. 如果是的話, 那么每次都容器中解析出來的對象都是同一對象實例, 否則每次都是重新New 的一個實例. 一般說來, 對於一些全局服務以及共享狀態等我們會將其注冊為單例模式, 對於那些其被每次都需要拿一個新的實例去注入的依賴項我們將其按照非單例模式的形式注冊.

5. 使用IUnityContainer 還是IContainerFacade

      他們分別位於 Microsoft.Practices.Composite 和Microsoft.Practices.Unity 命名空間下, 雖然都可以作為容器的高層接口,但使用IUnityContainer 基本上就意味着直接使用Unity 容器(Unity 項目中的那個依賴注入容器), 而使用IContainerFacade 則意味着你可以兼容格式各樣的容器. 但, 我們知道IContainerFacade 基本上只提供了一個功能" 解析", 但就一個基本的依賴注入容器而言, 往往不僅僅是這一個功能, 比如至少還要有類型注冊、實例注冊等等。所以在大多數情況下推薦使用IUnityContainer ,從開源項目"StockTraderRI” 中我們可以看到這一點, 除非是下面的情況之一:

  • l 你作為ISV( 獨立軟件供應商,independent software vendor) 編寫一些提供多容器支持的服務
  • l 你的服務被用到多容器的系統中

(在最新的版本4.1中已經有了新的容器MEF(Managed Extensibility Framework)可供選擇)

6. 代碼配置還是配置文件配置

      關於這個問題, 我引用一下 Martin Fowler 的一段話為大家提供一些參考:

     “代碼配置 vs. 配置文件另一個問題相對獨立,但也經常與其他問題牽涉在一起:如何配置服務的組裝,通過配置文件還是直接編碼組裝?對於大多數需要在多處部署的應用程序來說,一個單獨的配置文件會更合適。配置文件幾乎都是XML 文件,XML 也的確很適合這一用途。不過,有些時候直接在程序代碼中實現裝配會更簡單。譬如一個簡單的應用程序,也沒有很多部署上的變化,這時用幾句代碼來配置就比XML 文件要清晰得多。

與之相對的,有時應用程序的組裝非常復雜,涉及大量的條件步驟。一旦編程語言中的配置邏輯開始變得復雜,你就應該用一種合適的語言來描述配置信息,使程序邏輯變得更清晰。然后,你可以編寫一個構造器(builder )類來完成裝配工作。如果使用構造器的情景不止一種,你可以提供多個構造器類,然后通過一個簡單的配置文件在它們之間選擇。

     我常常發現,人們太急於定義配置文件。編程語言通常會提供簡捷而強大的配置管理機制,現代編程語言也可以將程序編譯成小的模塊,並將其插入大型系統中。如果編譯過程會很費力,腳本語言也可以在這方面提供幫助。通常認為,配置文件不應該用編程語言來編寫,因為它們需要能夠被不懂編程的系統管理人員編輯。但是,這種情況出現的幾率有多大呢?我們真的希望不懂編程的系統管理人員來改變一個復雜的服務器端應用程序的事務隔離等級嗎?只有在非常簡單的時候,非編程語言的配置文件才有最好的效果。如果配置信息開始變得復雜,就應該考慮選擇一種合適的編程語言來編寫配置文件”。

 

自己實踐Prism(Comprise Application Guidance for WPF)遇到的問題:

問題 1 :重寫啟動類UnityBootstrapper時遇到Resolve類型不匹配問題。

描述:在添加了命名空間:

using Microsoft.Practices.Prism.Modularity;

using Microsoft.Practices.Prism.UnityExtensions;

以后(無異常)在重寫protected override DependencyObject CreateShell()

時遇到:

clip_image002

函數Reslove類型不匹配問題。再接辦法添加對命名空間:

using Microsoft.Practices.Unity;的引用。
原因:該函數的重載版本是在這個命名空間下定義

(未完....待續....)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM