[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的独立介绍。
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。