摘要:[WPF] MVVM Plugin模式
動機 :
Plugin是在軟體系統內增加功能的功能。
如果在軟體系統加入Plugin功能,能提高軟體系統的重用性。
加入Plugin功能的軟體系統在開發完成之後。
如果需要額外加入功能,不用變更已完成的軟體系統就能加入新功能。
並且因為不用變更已完成的軟體系統,也就避免了修改軟體系統會產生的風險。
在MVVM的架構下View跟ViewModel各自獨立,做Plugin功能也就變得比較複雜。
必須要View跟ViewModel各自都有Plugin功能然後再互相組合,才能完成MVVM Plugin的功能。
本篇文章記錄在WPF上,如何實做MVVM Plugin。
為自己做個紀錄,也希望能幫助到有需要的開發人員。
結構 :
首先我們來看下圖,說明了MVVM Plugin的結構。
圖中說明了整個結構包含的專案,並且也可以看到它們之間的相依關係。
而MvvmPlugin.dll是整個Plugin的主要系統,MvvmPlugin.View與MvvmPlugin.ViewModel則是,主要系統之外要掛載的部份。
接著看看下面兩張圖,說明了MvvmPlugin.dll的物件跟畫面。
由圖中可以看出來,整個專案裡的物件。
可以分成兩大類,分別代表MVVM的View跟ViewModel。
代表View的部份負責實做,畫面如何呈現的功能。
代表ViewModel的部份是實做,畫面要呈現甚麼。
在View跟ViewModel之間,則是用WPF的Binding功能來做連接。
而在ViewModel裡面,也可以看到是由AnchorViewModel來負責建立IWorkspaceViewModel。
並且表單畫面會劃分為兩個區塊,
左邊區塊裡的<<WPF ItemsControl>>
-顯示所繫結的所有<<class>>AnchorViewModel。
-只要增加<<class>>AnchorViewModel的數量,就會增加畫面上的按鈕數量。
-使用<<WPF ItemTemplate>>來顯示繫結的<<class>>AnchorViewModel。
右邊區塊裡的<<WPF ContentControl>>
-則會顯示所繫結的一個<<inteface>>IWorkspaceViewModel。
-只要換掉<<inteface>>IWorkspaceViewModel的物件,就會變更畫面上的表單。
回頭看上一段落右邊區塊的說明,會發現沒有描述<<inteface>>IWorkspaceViewModel採用甚麼Template來顯示。
我們另外再參考下一張的圖片說明,
可以看到<<WPF ContentControl>>,使用外部Template來顯示繫結的<<inteface>>IWorkspaceViewModel。
至於系統裡有哪些外部Template、<<inteface>>IWorkspaceViewModel要用外部Template來顯示,則是由<<xaml>>MainWindow.view.config.xaml所設定。
而有哪些外部用來生成<<inteface>>IWorkspaceViewModel的<<class>>AnchorViewModel要顯示,則是由<<xaml>>MainWindow.viewmodel.config.xaml來設定。
在這兩個檔案資料內加入新增功能的資料,就可以完成MVVM Plugin要加入功能的功能。
實做 :
範列下載 :
範例的程式碼較多,實做說明請參照範例程式內容。
範例程式點此下載
MvvmPlugin.dll :
MvvmPlugin.dll是整個系統的主要結構。
提供了可執行的應用程式外殼,並且也開放Plugin的功能用來掛載系統。
主要參與者有:
MainWindowViewModel
-整個系統的主要ViewModel。
-提供AnchorCollection及Workspace,給MainWindowView.xaml做Binding。
-收到AnchorViewModel發出的WorkspaceViewModel事件時,用新的WorkspaceViewModel替換舊的。
-使用XamlReader頗析外部MainWindow.viewmodel.config.xaml檔案,當作AnchorCollection內容的來源。
//使用XamlReader頗析外部MainWindow.viewmodel.config.xaml檔案,當作AnchorCollection內容的來源。
private IEnumerable<AnchorViewModel> CreateAnchorCollection(string anchorConfigFile)
{
#region Require
if (string.IsNullOrEmpty(anchorConfigFile) == true) throw new ArgumentNullException();
#endregion
// Result
List<AnchorViewModel> anchorList = new List<AnchorViewModel>();
// Create
ResourceDictionary resourceDictionary = this.CreateResourceDictionary(anchorConfigFile);
foreach (object resource in resourceDictionary.Values)
{
AnchorViewModel anchorViewModel = resource as AnchorViewModel;
if (anchorViewModel != null)
{
anchorViewModel.WorkspaceOpened += delegate(IWorkspaceViewModel workspace)
{
IWorkspaceViewModel oldWorkspace = this.Workspace;
this.Workspace = workspace;
if (oldWorkspace != null) oldWorkspace.Dispose();
};
anchorList.Add(anchorViewModel);
}
}
// return
return anchorList;
}
private ResourceDictionary CreateResourceDictionary(string resourceDictionaryFile)
{
#region Require
if (string.IsNullOrEmpty(resourceDictionaryFile) == true) throw new ArgumentNullException();
#endregion
// Require
if (File.Exists(resourceDictionaryFile) == false) throw new ArgumentException(string.Format("File is not existed : {0}", resourceDictionaryFile));
// Create ResourceDictionary
FileStream fileStream = new FileStream(resourceDictionaryFile, FileMode.Open);
ResourceDictionary resourceDictionary = XamlReader.Load(fileStream) as ResourceDictionary;
// Return
return resourceDictionary;
}
MainWindow.xaml
-整個系統的主要View。
-直接在xaml內,建立MainWindowViewModel並且綁定。
-與MainWindowViewModel提供的AnchorCollection及Workspace,做Binding。
-參考外部ResourceDictionary的方式,讀取MainWindow.view.config.xaml,作為Binding WorkspaceViewModel的Template。
<!--參考外部ResourceDictionary的方式,讀取MainWindow.view.config.xaml-->
<Window.Resources>
<ResourceDictionary Source="pack://siteoforigin:,,,/MainWindow.view.config.xaml" />
</Window.Resources>
<!--直接在xaml內,建立MainWindowViewModel並且綁定-->
<Window.DataContext>
<MvvmPlugin:MainWindowViewModel />
</Window.DataContext>
IWorkspaceViewModel
-掛載進MvvmPlugin.dll的外部WorkspaceViewModel,要實做的介面。
-與外部Template做Binding來顯示。
AnchorViewModel
-掛載進MvvmPlugin.dll的外部AnchorViewModel,要實做的介面。
-提供Title、ExecuteCommand,給MainWindowView.xaml做Binding。
-觸發時建立新的WorkspaceViewModel,發出事件通知MainWindowViewModel。
MvvmPlugin.ViewModel.dll、MvvmPlugin.View.dll :
MvvmPlugin.ViewModel.dll是要掛載的ViewModel實做,職責除了提供要掛載進系統的功能之外也負擔了如何生成的職責。
MvvmPlugin.ViewModel.dll是要掛載的View實做,職責主要是提供Binding的ViewModel的外觀。
之所以沒有將這兩個專案做合併,是為了突顯在MVVM架構下View是可以獨立做抽換的。
主要參與者有:
AAnchorViewModel
-負責生成要掛載的ViewModel
AWorkspaceViewModel
-實際要掛載的ViewModel
AWorkspaceView.xaml
-實際要掛載的View
後記 :
在這個模式裡,其實還少了一塊Model的Plugin功能。
因為Model的Plugin這個功能,相對於MVVM Plugin是比較獨立的模式。
在後續的文章裡,將會有Model Plugin的獨立介紹。
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。