本篇是接着上篇Windows phone 應用開發[6]-Managed Extensibility Framework應用程序擴展 基礎之上而來. 關於Managed Extensibility FrameWork[MEf]基礎概念這里不再贅述.MEF 作為.NET 4一部分.同時也支持Silverlight4 版本.但因目前官方並沒有推出Managed Extensibility Framework For Windows phone 版本. 對Codeplexhttp://mef.codeplex.com/上類庫並不支持Windows phone. 這里介紹另外一種方式在Windows phone 中使用MEF.並以一個簡單實例 拋磚引玉.
在開始介紹之前如果你對MEf For Windows phone 中使用存在問題和相關的技術瓶頸 想了解一番可以閱讀如下文章:
MEf For Windows phone By damonpayne:
http://www.damonpayne.com/post/2010/06/25/MEF-for-Windows-Phone-7.aspx
這篇文章中作者Damonpayne很詳細闡述了MEF 發展的過程以及相對Windows phone 開發不支持的一些相關特性類似:System.Reflection.Emit剔除. 針對MEF操作同樣也不支持IQueryable接口的類Linq操作等.並提出自己的一套在Windows phone 中使用MEF的解決方案.可惜的是作者直接給一個封裝不完整DLL.並刪除下載頁面.經過實際代碼嘗試Damonpayne給出的一條DeadWay.
而欣慰的總是有人在無意間給你額外的驚喜. 在我看Microsoft Silverlight Analytics Framework[MSAF]源碼時意外的發現.這個開源框架中盡然移植一個Windows phone 版本的MEF:
MSAF分別針對原ComponentModel和Composition.Initialization空間做了Windows phone 類庫的移植. 而如上類庫正是MEF的核心.MSAF框架主要作用是提供了一種標准方法在Windows phone應用程序中添加對使用情況加以跟蹤並支持第三方數據分析的功能.這個我會下篇中Windows phone采集用戶數據和行為分析上講解.
Well 似乎MEF支持問題就如此意外的迎刃而解了.如下來做一個簡單實例來驗證.現在提出一個簡單需求我們宿主程序中要通過MEF方式集成一個管理分類的組件.組件和Windows phone宿主程序關系如下:
以創建一個Windows phone的類庫形式來實現這個組件 創建類庫並命名-MEFCommon.Data.首先添加MEF 類庫的引用:
分別引用了元MSAF源碼中System.ComponentModel和Composition.Initialization兩個DLL.創建一個封裝組件分類操作的接口IAppCatalog 添加引用如下:
1: using System.ComponentModel;
2: using System.ComponentModel.Composition;
3: using System.ComponentModel.Composition.Hosting;
接口中分別定義兩個操作方法.一個用來添加分類方法 另外一個獲取所有分類的數據方法:
1: public interface IAppCatalog
2: {
3: bool AddCatalog(AppCatalog newAppCatalog);
4: List<AppCatalog> GetAllAppCatalogList();
5: }
創建AppCatalogOperator類實現該接口:
1: [Export(typeof(IAppCatalog))]
2: public class AppCatalogOperator:IAppCatalog
3: {
4: public List<AppCatalog> OperatorCatalogList = new List<AppCatalog>();
5:
6: public bool AddCatalog(AppCatalog newAppCatalog)
7: {
8: //No Check Reply
9: if(newAppCatalog!=null)
10: this.OperatorCatalogList.Add(newAppCatalog);
11: return true;
12: }
13:
14: public List<AppCatalog> GetAllAppCatalogList()
15: {
16: this.OperatorCatalogList.Clear();
17: this.OperatorCatalogList.Add(new AppCatalog() { CatalogName = "Music+Video", CatalogNote = "Important for Common User" });
18: this.OperatorCatalogList.Add(new AppCatalog() { CatalogName = "Game", CatalogNote = "Different Kind of Game platform" });
19: this.OperatorCatalogList.Add(new AppCatalog() { CatalogName = "Book", CatalogNote = "Reader" });
20:
21: return this.OperatorCatalogList;
22:
23: }
24: }
同時在該類以IAppCatalog接口作為契約名向MEF開放.該類一方面提供分類的全部數據同時能夠執行添加分類的操作.如下我們要在一個Windows phone宿主程序中用到分類組件提供分類數據.綁定UI上顯示,定義一個UI上可操作的ViewModel:
1: public class AppCatalog_ViewModel:BasicViewModel
2: {
3: public AppCatalog_ViewModel()
4: {
5: CompositionInitializer.SatisfyImports(this);
6: }
7:
8: public ObservableCollection<AppCatalog> appCatalogCollection = new ObservableCollection<AppCatalog>();
9: public ObservableCollection<AppCatalog> AppCatalogCollection
10: {
11: get { return this.appCatalogCollection; }
12: set
13: {
14: this.appCatalogCollection = value;
15: base.NotifyPropertyChangedEventHandler("AppCatalogCollection");
16: }
17: }
18:
19: [Import(typeof(IAppCatalog))]
20: public IAppCatalog CurrentCatalogData { get; set; }
21:
22: public void LoadAppCatalogData()
23: {
24: if (this.CurrentCatalogData != null)
25: {
26: if (this.CurrentCatalogData.GetAllAppCatalogList().Count > 0)
27: {
28: var catalogList = this.CurrentCatalogData.GetAllAppCatalogList();
29: this.appCatalogCollection.Clear();
30: catalogList.ForEach(x => { this.appCatalogCollection.Add(x); });
31: }
32: }
33: }
34: }
首先在ViewModel定義一個以Import標識IAppcatalog類型屬性用來接收組件中通過MEF需要傳遞的數據.其實這就是宿主程序一個擴展點.同樣這個Windows phone宿主程序需要添加對組件類庫和MEF 引用.現在有了組件的數據 和宿主程序的擴展點接入.Well,要實現組件與宿主程序之間通信則需要通過MEF建立組合的關聯關系. 而這個組合關系引用需要在應用程序啟動是調用.上篇中我們采用是一個Console應用程序直接寫在Main方法.作為Windows phone當然也可以直接寫在Launching和Activated事件中.當然最好的方式是創建一個實現IApplicationService接口的應用程序服務.可以獲得更好的代碼封裝和關注分離的效果 創建Service如下:
1: public class MEFAppCatalogService:IApplicationService
2: {
3: public MEFAppCatalogService()
4: {
5: CompositionHost.Initialize
6: (new AssemblyCatalog(Application.Current.GetType().Assembly),
7: new AssemblyCatalog(typeof(MEFCommon.Data.IAppCatalog).Assembly));
8: }
9:
10: public void StartService(ApplicationServiceContext context) { }
11:
12: public void StopService() { }
13: }
在構造方法中.調用CompositionHost對象Initialize()方法.相對於上篇我們直接通過定義CompositionContainer容器的方式.而目前CompositionHost對象提供用於控制 CompositionInitializer 所使用的容器的靜態方法.二者效果是一致的. Initialize()方法在構造是可以接受任意數量的目錄. 這和AggregateCatalog對象指定MEF解析范圍是一致的.目錄適用於定義MEF在解析類型是需要檢索的位置范圍.而引用的對象AssemblyCatalog則分別提供對分類組件類庫和宿主應用程序的靜態引用.
接着指定運行的位置.向App.xaml內Application.ApplicationLifetimeObjects集合添加一個MEFAppCatalogService實例.則宿主程序會在任何應用程序代碼運行之前調用服務的Initialize方法來初始化MEF的管理容器.
1: <Application.ApplicationLifetimeObjects>
2: <!--Required object that handles lifetime events for the application-->
3: <shell:PhoneApplicationService
4: Launching="Application_Launching" Closing="Application_Closing"
5: Activated="Application_Activated" Deactivated="Application_Deactivated"/>
6:
7: <mef:MEFAppCatalogService></mef:MEFAppCatalogService>
8: </Application.ApplicationLifetimeObjects>
最后就是通過在ViewModel夠着方法中調用CompositionInitializer對象的SatisfyImports()方法實現填充指定組件的導入.[observerCollection集合已經綁定UIlistBox控件]:
1: //組件導入
2: CompositionInitializer.SatisfyImports(this);
現在運行Windows phone應用程序看分類組件提供分類數據能拿到:
ok.成功通過MEF框架宿主程序自動感知的靈活方式添加組件並獲得組件提供分類數據.證明MASF提供MEF類庫是可行的.到這了不禁有人會問相對上篇難道沒有其他方式建立宿主和組件之間組合關系?答案是肯定的.
只不過如果我們不以Service形式.我們建立組件關系.這段建立組合關系代碼放在那? 應該采用什么方式來組合? 來看看Mainpage綁定ViewModel時:
1: //Bind ViewModel
2: private AppCatalog_ViewModel appcatalog_ViewModel = null;
3: void MainPage_Loaded(object sender, RoutedEventArgs e)
4: {
5: if (this.appcatalog_ViewModel == null)
6: this.appcatalog_ViewModel = new AppCatalog_ViewModel();
7: this.appcatalog_ViewModel.LoadAppCatalogData();
8: this.DataContext = appcatalog_ViewModel;
9: }
可見每次都會Load時重新構造並調用ViewModel構造方法.考慮可以把組合關系代碼放在這.CompositionInitializer對象提供了對MEF容器的靜態方法控制.當然我們也可以像上篇一樣顯示的定義一個CompositionContainer 容器的方式直接管理. 可以把原ViewModel中代碼替換成:
1:
2: //顯示建立關聯關系
3: AggregateCatalog mefCatalog = new AggregateCatalog();
4: mefCatalog.Catalogs.Add(new AssemblyCatalog(Application.Current.GetType().Assembly));
5: mefCatalog.Catalogs.Add(new AssemblyCatalog(typeof(MEFCommon.Data.IAppCatalog).Assembly));
6:
7: CompositionContainer mefContainer = new CompositionContainer(mefCatalog);
8: mefContainer.ComposeParts(this);
通過AggregateCAtalog顯示指定MEF解析類型需要檢索的位置范圍。編譯運行發現效果是一致的.
關於這兩種方式.最簡潔使用就是CompositionInitializer對象提供靜態控制.把更多的工作交給MEF自身去做. 另外一個好處就是可以隨時隨地在程序中添加MEF組合關系和檢索位置的范圍.這是顯示定義CompositionContainer 容器方式所無法直接做到的.
Well關於MEF終於歷經多次驗證終於能夠成功運行在Windows phone應用程序中.本篇只是介紹簡單使用方法.目的是拋磚引玉.體現MEF也能夠在Windows phone擴展組件能力和靈活的方式都值得我們在實際項目加以實踐. 算是提出MEF 在Windows phone中使用一種解決方案.如果任何問題請在評論中提出.如下會給出本篇實例源碼和MASF MEF For windows phone版本的DLL.
本篇實例代碼:/Files/chenkai/MEFSampleDemo.rar
MEF For windows Phone DLL文件:/Files/chenkai/ComponentModelDLL.rar
參考資料: