SwordNet 是一个基于 Prism + Unity 的模块化 WPF 桌面应用框架,支持插件化加载、Ribbon 界面和可停靠布局。项目由 TheSeed(核心容器)、Excalibur(功能模块)、Tools(主程序)三个子项目组成,旨在为桌面应用提供一套可复用的插件化架构基础。
GitHub 地址:https://github.com/3gbywork/SwordNet
核心功能#
| 功能 | 说明 |
|---|---|
| 模块化架构 | 基于 Prism 的模块加载机制,支持动态发现和加载功能模块 |
| 插件化界面 | 通过 RibbonButtonBaseInfo 和 IRibbonButtonInfo 动态生成 Ribbon 菜单 |
| 可停靠布局 | 基于 AvalonDock 实现多文档停靠,支持 AvalonDock / TabControl 两种布局切换 |
| 依赖注入 | 使用 Unity 容器管理模块和服务生命周期 |
| 进程与断点工具 | 集成 ProcessOperator / BreakpointOperator / ConsoleOperator,通过 P/Invoke 调用 Win32 API |
| 多日志框架 | 同时集成 NLog 和 log4net,通过 Logger 统一封装 |
| 多语言支持 | 基于 ResX 资源文件实现中英文界面切换 |
技术栈#
| 层面 | 技术 |
|---|---|
| UI 框架 | WPF (.NET Framework 4.5.2) |
| 架构 | Prism 6.3 + Unity 4.0.1 |
| UI 组件 | MahApps.Metro 1.5 + Fluent.Ribbon 5.0 + AvalonDock 3.3 |
| 数据持久化 | Entity Framework 6 + SQLite |
| 日志 | NLog 4.5 + log4net 2.0 |
| 配置 | XSD 强类型配置 + XML |
| 互操作 | P/Invoke(WinApi.Net) |
| 本地化 | ResX(zh-Hans) |
架构设计#
整体架构#
SwordNet 由三个子项目组成,从底层容器到上层应用逐层解耦:
flowchart TB
subgraph Tools["Tools(主程序)"]
Boot["Bootstrapper"]
Main["MainWindow"]
Ribbon["RibbonUI"]
Boot ~~~ Main ~~~ Ribbon
end
subgraph Excalibur["Excalibur(功能模块)"]
Process["ProcessOperator"]
Break["BreakpointOperator"]
Console["ConsoleOperator"]
Views["Views / Config"]
Process ~~~ Break ~~~ Console ~~~ Views
end
subgraph TheSeed["TheSeed(核心容器)"]
IView["IView 接口"]
Module["模块加载"]
Service["服务注册"]
IView ~~~ Module ~~~ Service
end
Tools --> Excalibur
Excalibur --> TheSeed分层职责#
| 项目 | 职责 | 关键类型 |
|---|---|---|
| TheSeed | 核心容器,定义接口,负责模块加载和服务注册 | IView、ModuleInfo |
| Excalibur | 功能模块,实现具体业务功能 | ProcessOperator、BreakpointOperator、ConsoleOperator |
| Tools | 主程序,提供启动引导和主界面 | Bootstrapper、MainWindow、RibbonUI |
启动流程#
应用启动时,由 Bootstrapper 完成容器初始化和模块加载:
sequenceDiagram
participant App as App.xaml
participant Boot as Bootstrapper
participant Container as Unity Container
participant Module as 模块加载器
participant Shell as MainWindow
App->>Boot: Run()
Boot->>Container: 创建 Unity 容器
Boot->>Container: 注册核心服务
Boot->>Module: 扫描并加载模块
Module->>Container: 注册模块服务与视图
Boot->>Shell: 显示主窗口
Shell->>Shell: 初始化 Ribbon 与 ContentPanel插件化界面机制#
Ribbon 菜单和内容面板均通过接口抽象,支持动态扩展:
flowchart LR
subgraph Models["接口定义"]
IRibbon["IRibbonButtonInfo"]
IContent["IContentPanel"]
end
subgraph Impl["具体实现"]
RibbonInfo["RibbonButtonBaseInfo"]
Avalon["AvalonDock"]
Tab["TabControl"]
end
subgraph Commands["命令层"]
AddInCmd["AddInCommand"]
PanelCmd["ContentPanelCommand"]
end
IRibbon --> RibbonInfo
IContent --> Avalon
IContent --> Tab
AddInCmd --> IRibbon
PanelCmd --> IContent机制说明:
-
IRibbonButtonInfo定义按钮元数据,模块只需提供实现即可自动出现在 Ribbon 菜单 -
IContentPanel抽象内容容器,AvalonDock和TabControl是两种实现,可运行时切换 -
AddInCommand和ContentPanelCommand分别处理菜单和面板的交互命令
模块加载机制#
模块通过 ModuleInfo 描述,由容器按需加载:
flowchart TB
A["扫描模块目录"] --> B["读取 ModuleInfo"]
B --> C{"模块是否启用?"}
C -- 是 --> D["创建模块实例"]
C -- 否 --> E["跳过"]
D --> F["注册服务到 Unity"]
F --> G["注册视图到 Region"]
G --> H["模块加载完成"]配置热更新机制#
SwordNet 的配置热更新能力,来源于自研 NuGet 库 CommonUtility.Config。该库提供了 IXmlConfig 接口和 XmlConfigurator.ConfigAndWatch 方法,使配置文件的实时监控变得非常简单。
库的核心设计#
using System.Xml;
namespace CommonUtility.Config;
public interface IXmlConfig
{
void Config(XmlElement xmlElement);
}接口只有一个方法 Config,接收 XmlElement 参数。任何配置实体实现该接口后,只需关注如何从 XML 中解析数据,不需要关心文件监控、读取、反序列化等细节。
业务侧实现#
SwordNet 中的 ConsoleConfigEntity 实现了 IXmlConfig,在 Config 方法中完成 XML 解析,并通过自定义事件通知订阅者:
using CommonUtility.Config;
using Excalibur.Models;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Excalibur.Config
{
class ConsoleConfigEntity : IXmlConfig
{
public delegate void ConfigChangedHandler(ICollection<ConsoleModel> consoles);
public event ConfigChangedHandler OnConfigChanged;
public void Config(XmlElement xmlElement)
{
if (OnConfigChanged != null)
{
var consoles = GetConfigFromXml(xmlElement);
OnConfigChanged.Invoke(consoles);
}
}
private ICollection<ConsoleModel> GetConfigFromXml(XmlElement xmlElement)
{
var result = new SortedSet<ConsoleModel>();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Agents));
using (StringReader stringReader = new StringReader(xmlElement.OuterXml))
{
if (xmlSerializer.Deserialize(stringReader) is Agents agents
&& null != agents.Agent)
{
foreach (var agent in agents.Agent)
{
result.Add(new ConsoleModel(agent));
}
}
}
return result;
}
}
}使用方式#
ConsoleConfigEntity entity = new ConsoleConfigEntity();
entity.OnConfigChanged += OnConfigChanged;
XmlConfigurator.ConfigAndWatch(entity, new FileInfo("config/ConsoleConfig.xml"));工作流程#
sequenceDiagram
participant App as 应用
participant Entity as ConsoleConfigEntity
participant Configurator as XmlConfigurator
participant FS as 文件系统
App->>Entity: new ConsoleConfigEntity()
App->>Entity: 订阅 OnConfigChanged
App->>Configurator: ConfigAndWatch(entity, fileInfo)
Configurator->>FS: 读取 XML
Configurator->>Entity: 调用 Config(xmlElement)
Entity->>Entity: 解析 XML,触发 OnConfigChanged
Entity-->>App: 通知订阅者
Note over FS: 用户修改配置文件
FS-->>Configurator: 触发文件变更事件
Configurator->>Entity: 再次调用 Config(xmlElement)
Entity-->>App: 通知订阅者,配置立即生效机制说明#
| 组件 | 归属 | 职责 |
|---|---|---|
IXmlConfig |
CommonUtility.Config |
定义配置解析接口,业务实体实现 Config 方法 |
XmlConfigurator.ConfigAndWatch |
CommonUtility.Config |
加载配置 + 启动文件监控,变更时调用 Config |
ConsoleConfigEntity |
SwordNet | 实现 IXmlConfig,解析 XML 并触发业务事件 |
OnConfigChanged |
SwordNet | 业务自定义事件,通知订阅者配置已更新 |
设计优势#
-
库与业务解耦:通用能力沉淀在 NuGet 库,业务项目只需实现
IXmlConfig接口 -
职责单一:库负责文件监控和读取,业务负责解析和通知
-
实时生效:修改配置文件后无需重启应用
-
强类型:通过
XmlSerializer反序列化为强类型模型 -
可复用:任何 .NET 项目都可以引用
CommonUtility.Config获得同样能力
配置与日志#
项目使用 XSD 定义强类型配置,并同时支持两种日志框架:
flowchart LR
subgraph Config["配置"]
XSD["XSD 定义"]
XML["XML 配置"]
CS["强类型 C# 类"]
XSD --> CS
XML --> CS
end
subgraph Log["日志"]
Logger["Logger 统一接口"]
NLog["NLog"]
Log4["log4net"]
Logger --> NLog
Logger --> Log4
end说明:
-
XSD 生成强类型 C# 类,避免硬编码配置键
-
Logger封装统一日志接口,底层可切换 NLog 或 log4net
项目亮点#
-
Prism 模块化落地:完整的 Bootstrapper 启动流程、模块动态加载、Region 管理
-
Ribbon + Metro 双 UI 框架:MahApps.Metro 提供现代化窗口,Fluent.Ribbon 提供 Office 风格菜单
-
可停靠布局:AvalonDock 实现多文档停靠,支持布局切换和状态持久化
-
强类型配置:通过 XSD 定义配置结构,生成强类型 C# 类,避免硬编码
-
双日志框架:NLog + log4net 同时集成,通过统一接口屏蔽差异
-
P/Invoke 封装:基于 WinApi.Net 封装进程、断点、控制台等系统级操作
截图#
Dock 布局#

Tab 布局#

XSD 配置智能提示#
