SwordNet

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 布局#

Dock Layout

Tab 布局#

Tab Layout

XSD 配置智能提示#

XSD 配置智能提示

❤️ 如果这篇文章对你有帮助,欢迎赞助支持我继续维护 ❤️

☕ Support me ⚡ 爱发电赞助