在软件开发中,模块化架构是一种常见的设计模式,它允许将应用程序分解成独立的、可互换的模块。这种设计使得应用程序更加灵活,易于维护和扩展。本文将介绍如何利用Prism框架和Modern UI forWPF(MUI)工具包,创建一个具有插件架构的WPF应用程序。
假设有一个客户,他需要开发一个桌面应用程序,该程序需要根据不同的企业办公室展示不同的功能集。可能会回想起之前参与的项目,或者可能会想到使用开源项目来创建具有现代外观和感觉的WPF应用程序。本文将展示如何使用Prism和MUI库来解决这个问题。
提出的插件架构的核心思想包括:
如果查看MUI演示项目的MainWindow.xaml源代码,将看到主菜单是如何静态构建的。主窗口的主菜单是一个依赖属性,名为MenuLinkGroups。它返回LinkGroupCollection类的实例,该类继承自ObservableCollection
public interface ILinkGroupService
{
LinkGroup GetLinkGroup();
}
核心模块定义了ILinkGroupService接口。它声明,如果一个模块想要在主菜单上插入一个选项,它可以通过实现GetLinkGroup()方法来实现,该方法实际上返回一个LinkGroup实例。
public class LinkGroupService : ILinkGroupService
{
public LinkGroup GetLinkGroup()
{
LinkGroup linkGroup = new LinkGroup
{
DisplayName = "Module One"
};
linkGroup.Links.Add(new Link
{
DisplayName = "Module One",
Source = new Uri($"/DM.ModuleOne;component/Views/{nameof(MainView)}.xaml", UriKind.Relative)
});
return linkGroup;
}
}
现在,需要能够动态加载模块,为每个模块创建ILinkGroupService接口的实例,并将导出的选项插入到主菜单中。这是在Bootstrapper类的ConfigureModuleCatalog()方法中实现的代码的功能。
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = MODULES_PATH };
}
protected override void ConfigureModuleCatalog()
{
var directoryCatalog = (DirectoryModuleCatalog)ModuleCatalog;
directoryCatalog.Initialize();
linkGroupCollection = new LinkGroupCollection();
var typeFilter = new TypeFilter(InterfaceFilter);
foreach (var module in directoryCatalog.Items)
{
var mi = (ModuleInfo)module;
var asm = Assembly.LoadFrom(mi.Ref);
foreach (Type t in asm.GetTypes())
{
var myInterfaces = t.FindInterfaces(typeFilter, typeof(ILinkGroupService).ToString());
if (myInterfaces.Length > 0)
{
var linkGroupService = (ILinkGroupService)asm.CreateInstance(t.FullName);
var linkGroup = linkGroupService.GetLinkGroup();
linkGroupCollection.Add(linkGroup);
}
}
}
var moduleCatalog = (ModuleCatalog)ModuleCatalog;
moduleCatalog.AddModule(typeof(Core.CoreModule));
}
首先创建Bootstrapper.ModuleCatalog作为DirectoryModuleCatalog并初始化模块目录。然后遍历发现的模块。对于每一个模块,查找实现ILinkGroupService接口的类型。如果找到这样的类型,那么创建一个实例并调用其GetLinkGroup()方法。返回的LinkGroup实例然后插入到集合中,该集合在创建Shell时传递给它:
protected override DependencyObject CreateShell()
{
Shell shell = Container.Resolve();
if (linkGroupCollection != null)
{
shell.AddLinkGroups(linkGroupCollection);
}
return shell;
}
Shell.AddLinkGroups()方法定义如下:
public void AddLinkGroups(LinkGroupCollection linkGroupCollection)
{
CreateMenuLinkGroup();
foreach (LinkGroup linkGroup in linkGroupCollection)
{
this.MenuLinkGroups.Add(linkGroup);
}
}
其中CreateMenuLinkGroup()方法创建主菜单的静态通用选项,而foreach循环创建动态选项。