创建具有插件架构的WPF应用程序

在软件开发中,模块化架构是一种常见的设计模式,它允许将应用程序分解成独立的、可互换的模块。这种设计使得应用程序更加灵活,易于维护和扩展。本文将介绍如何利用Prism框架和Modern UI forWPF(MUI)工具包,创建一个具有插件架构的WPF应用程序。

假设有一个客户,他需要开发一个桌面应用程序,该程序需要根据不同的企业办公室展示不同的功能集。可能会回想起之前参与的项目,或者可能会想到使用开源项目来创建具有现代外观和感觉的WPF应用程序。本文将展示如何使用Prism和MUI库来解决这个问题。

架构

提出的插件架构的核心思想包括:

  • 将所需的项目模块放入一个目录中(或者全部放入并在加载时进行过滤)。
  • 从模块目录动态加载项目模块。
  • 每个模块都为主菜单中的一个选项提供入口点。
  • 从加载的模块动态构建主菜单。
  • 主菜单中的第一个选项是固定的,对所有用户都是通用的。
  • 核心模块包含共享服务、存储库、DTO、数据模型定义等,它是静态加载的,可以被任何解决方案项目引用。

理解代码

如果查看MUI演示项目的MainWindow.xaml源代码,将看到主菜单是如何静态构建的。主窗口的主菜单是一个依赖属性,名为MenuLinkGroups。它返回LinkGroupCollection类的实例,该类继承自ObservableCollection。这意味着主菜单是一个可观察的链接组集合。每个LinkGroup代表菜单的一个入口点。因此,如果每个动态模块能够导出一个LinkGroup实例,所要做的就是将其添加到可观察的链接组集合中。这可以通过接口合同的形式来实现。

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循环创建动态选项。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485