Saury.Revit.Template
1.3.4
dotnet new install Saury.Revit.Template@1.3.4
Saury.Revit.Template
一个基于 MVVM 模式和依赖注入的 Revit 插件开发模板,同时支持 Revit 2024、Revit 2025、Revit 2026 和 Revit 2027。
特性
- 多版本支持 -
Debug_R24/Release_R24面向 Revit 2024,Debug_R25/Release_R25面向 Revit 2025,Debug_R26/Release_R26面向 Revit 2026,Debug_R27/Release_R27面向 Revit 2027 - 对应运行时 - Revit 2024 使用
net48,Revit 2025 / 2026 使用net8.0-windows,Revit 2027 使用net10.0-windows - MVVM 架构 - 使用 CommunityToolkit.Mvvm 实现 Model-View-ViewModel 模式
- 依赖注入 - 基于 Microsoft.Extensions.Hosting 的 DI 容器
- 依赖加载兜底 - 正常解析失败时从插件目录加载依赖;.NET Framework 使用
Assembly.LoadFrom,现代 .NET 使用主插件所在的AssemblyLoadContext - 薄命令入口 -
RevitCommandBase统一处理异常日志、执行结果和基础设施访问 - Ribbon 分层 -
Application只负责生命周期,Ribbon 创建集中在IApplicationUI - 泛型 Dialog 服务 - 支持
ShowDialog<TView>()模态窗口和Show<TView>()唯一非模态窗口 - Revit 上下文服务 - 插件启动时一次性注入
UIApplication,业务代码通过IRevitContext动态访问当前文档 - 外部事件服务 - 内置单请求
RevitEventService,非模态 WPF 可安全封送操作到 Revit API 上下文 - 独立 Data 项目 - 本地数据库基础设施放在
MyRevitPlugin.Data,主插件项目只通过 DI 引用 - 独立 UI 项目 -
MyRevitPlugin.Ui集中提供主题令牌、控件样式、转换器和资源字典,业务窗口不重复维护视觉基础设施 - 主题切换 - 内置浅色、深色和跟随 Windows 系统三种模式,已打开窗口和原生标题栏即时更新
- 中英本地化 - 设置页、代码侧文本和 Revit Ribbon 共用同一份中英文资源,切换后立即生效
- 本地 SQLite 数据库 - 基于 FreeSql,内置设置表、数据库初始化和备份服务
- 常用 Revit 扩展 - 内置轻量 Transaction 和 Collector 辅助方法
- 共享 Data 目录 - 所有 Revit 版本共用
%APPDATA%\MyRevitPlugin\Data,安装器可写入用户选择的数据目录 - 日志系统 -
LogProviderService接入 Microsoft.Extensions.Logging,按日期写入共享 Data 目录下的Logs\Saury.Revit.TemplateyyyyMMdd.log - MSI 安装器 - 内置 WixSharp per-user 安装器,支持版本选择、Revit 检测、运行阻断、升级和卸载时可选清除用户数据
- 自动混淆 - Release 安装包构建自动使用 ConfuserEx2 混淆自有插件程序集,Debug 和日常插件构建不混淆
- 自动替换 - 项目名称、命名空间、程序集名、addin 文件名和 GUID 自动生成替换
- 自动部署 - 默认构建后复制到当前用户的 Revit Addins 目录,可用
-p:DeployToRevit=false关闭 - 完整示例 - 包含居中的竖向 SettingsView,演示 MVVM、主题、本地化、持久化和 Ribbon 联动
安装模板
dotnet new install Saury.Revit.Template
使用模板
创建新项目
dotnet new saury-revit -n MyRevitPlugin
构建 Revit 2024
dotnet build MyRevitPlugin.slnx --configuration Debug_R24
构建 Revit 2025
dotnet build MyRevitPlugin.slnx --configuration Debug_R24
dotnet build MyRevitPlugin.slnx --configuration Debug_R25
构建 Revit 2026
dotnet build MyRevitPlugin.slnx --configuration Debug_R26
构建 Revit 2027
dotnet build MyRevitPlugin.slnx --configuration Debug_R27
构建部署行为
dotnet build MyRevitPlugin.slnx --configuration Debug_R25
dotnet build MyRevitPlugin.slnx --configuration Debug_R26
dotnet build MyRevitPlugin.slnx --configuration Debug_R27
默认构建会部署。需要只编译不复制时:
dotnet build MyRevitPlugin.slnx --configuration Debug_R27 -p:DeployToRevit=false
部署目标使用当前 Windows 用户的 %APPDATA%,按构建配置自动选择:
Debug_R24 / Release_R24 -> %APPDATA%\Autodesk\Revit\Addins\2024\
Debug_R25 / Release_R25 -> %APPDATA%\Autodesk\Revit\Addins\2025\
Debug_R26 / Release_R26 -> %APPDATA%\Autodesk\Revit\Addins\2026\
Debug_R27 / Release_R27 -> %APPDATA%\Autodesk\Revit\Addins\2027\
构建 MSI 安装器
dotnet build MyRevitPlugin.Installer/MyRevitPlugin.Installer.csproj --configuration Release
Release 安装器构建会自动构建 Release_R24 / Release_R25 / Release_R26 / Release_R27 四套插件产物,并使用 ConfuserEx2 混淆自有程序集后打包 MSI。普通插件构建和 Debug 安装器构建不会混淆。
安装器输出:
MyRevitPlugin.Installer/Output/MyRevitPlugin-Setup-1.0.0.msi
默认混淆规则:
rename
ctrl flow
ref proxy
anti debug
anti dump
anti ildasm
constants、invalid metadata、anti tamper、resources、typescramble、harden 默认禁用。详见 Docs/编译混淆.md。
卸载模板
dotnet new uninstall Saury.Revit.Template
项目结构
MyRevitPlugin/
├── MyRevitPlugin.slnx
├── MyRevitPlugin.Data/
│ ├── Compatibility/ # net48 编译器特性兼容 shim
│ ├── Configuration/ # 数据库配置、FreeSql 初始化、本地数据路径
│ ├── Entities/ # 模板级数据实体
│ ├── Extensions/ # AddLocalDataServices 注册入口
│ └── Services/
│ ├── Interfaces/ # 本地数据服务接口
│ ├── DatabaseBackupService.cs
│ └── PluginSettingService.cs
├── MyRevitPlugin.Ui/
│ ├── Controls/ # Button、Card、ComboBox、Switch、Slider 等控件样式
│ ├── Converters/ # 通用 WPF 转换器
│ ├── Enums/ # 实际视觉主题枚举
│ ├── Resources/ # 控件聚合字典和浅色/深色主题令牌
│ ├── ThemeManager.cs
│ └── MyRevitPlugin.Ui.csproj
├── MyRevitPlugin.Installer/
│ ├── MyRevitPlugin.Installer.csproj
│ └── build.cs # WixSharp MSI authoring
├── MyRevitPlugin/
│ ├── Compatibility/ # net48 record/init/required 编译器特性兼容 shim
│ ├── Commands/ # Revit 命令适配
│ │ ├── RevitCommandBase.cs
│ │ └── SettingsCommand.cs
│ ├── Extensions/ # DI、Ribbon、Transaction、Collector 扩展
│ ├── Infrastructure/ # Revit 宿主、窗口、外部事件和日志基础设施
│ │ ├── Interfaces/
│ │ │ ├── IApplicationUI.cs
│ │ │ ├── IDialogService.cs
│ │ │ ├── IRevitContext.cs
│ │ │ └── IRevitEventService.cs
│ │ ├── ApplicationUI.cs
│ │ ├── DialogService.cs
│ │ ├── RevitContext.cs
│ │ ├── RevitEventService.cs
│ │ ├── WindowThemeHelper.cs
│ │ └── LogProviderService.cs
│ ├── Localization/ # XAML 动态资源与代码侧文本查询
│ ├── Models/ # UiPreferences、主题与语言枚举
│ ├── Services/ # UiSettingsService、SystemThemeWatcher
│ ├── ViewModels/ # SettingsViewModel
│ ├── Views/ # SettingsView
│ ├── Resources/ # Ribbon/窗口图标和中英文资源
│ ├── Host.cs # Host 启停、日志、容器创建
│ └── Application.cs # Revit 插件生命周期入口
MVVM 架构说明
Model
数据模型和业务逻辑,纯 C# 类。
public sealed class UiPreferences
{
public string Theme { get; set; } = "Light";
public string Language { get; set; } = "zh-CN";
}
ViewModel
继承 ObservableObject,使用 [ObservableProperty] 和 [RelayCommand]。需要访问 Revit 上下文时注入 IRevitContext,需要访问外部系统时优先抽接口,避免 ViewModel 直接依赖静态 API。
public sealed class SettingsViewModel : ObservableObject
{
public SettingsViewModel(IUiSettingsService uiSettings)
{
SelectedTheme = uiSettings.ThemePreference;
SelectedLanguage = uiSettings.Language;
}
public ThemePreference SelectedTheme { get; set; }
public UiLanguage SelectedLanguage { get; set; }
}
View
XAML 界面通过构造函数注入 ViewModel。
public SettingsView(SettingsViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
Command
命令继承 RevitCommandBase。UIApplication 已在插件启动时注入,基类只负责异常、日志和基础设施访问,并提供 DialogService、RevitContext 与 RevitEventService。具体命令只表达自身动作,不默认为每个命令创建 CommandHandler 和接口;只有当命令背后出现可复用业务流程时,再抽成独立 service。
protected override Result ExecuteCore(ExternalCommandData commandData, ref string message, ElementSet elements)
{
DialogService.ShowDialog<SettingsView>();
return Result.Succeeded;
}
Dialog
窗口由 DI 创建,Dialog 服务负责解析 View、设置 Revit 主窗口 owner 并显示窗口:
public interface IDialogService
{
bool? ShowDialog<TView>() where TView : Window;
void Show<TView>() where TView : Window;
}
Show<TView>() 会保证同类型非模态窗口只保留一个实例,并把窗口定位到 Revit 当前激活视图左上角。
ExternalEvent
非模态窗口不能直接调用 Revit API。打开非模态窗口的命令先初始化外部事件,再显示窗口:
protected override Result ExecuteCore(ExternalCommandData commandData, ref string message, ElementSet elements)
{
RevitEventService.Initialize();
DialogService.Show<MyView>();
return Result.Succeeded;
}
ViewModel 注入 IRevitEventService 和 IRevitContext,通过单请求外部事件执行 Revit 操作:
revitEventService.Run("更新模型", () =>
{
var document = revitContext.ActiveDocument
?? throw new InvalidOperationException("当前没有打开的文档。");
document.RunTransaction("更新模型", () =>
{
// Revit API 写操作
});
}, ex => StatusText = $"执行失败:{ex.Message}");
同一时刻只允许一个请求等待或执行;后续请求会立即通过 onError 返回,不会在插件内部排队。
Ribbon
Application 不直接创建按钮。Ribbon UI 集中在 IApplicationUI / ApplicationUI,按钮通过 RibbonPanelExtensions.AddPushButton<TCommand>() 创建:
panel.AddPushButton<SettingsCommand>(
uiSettings.GetText("Ribbon.Settings.Text"),
uiSettings.GetText("Ribbon.Settings.ToolTip"),
RibbonPanelExtensions.LoadResourceImage("Resources/Icons/setting.png"));
主题与本地化
MyRevitPlugin.Ui只负责实际浅色/深色视觉资源和可复用控件,不依赖 Revit API。UiSettingsService是主题偏好、实际主题和界面语言的唯一业务状态源,设置保存在共享数据库的ui.preferences记录中。SystemThemeWatcher监听 Windows 应用主题,并把回调封送回 Revit UI Dispatcher。LocalizationManager同时服务 XAMLDynamicResource、代码侧字符串和 Ribbon 文本;只修改CurrentUICulture,不会改变数值输入解析使用的CurrentCulture。DialogService为窗口应用 Culture、主题标题栏和事件生命周期,设置页以 Revit 主窗口为 Owner 居中打开。- 新增窗口时合并
LocalizationDictionary、ThemesDictionary和ControlsDictionary,即可继承主题和语言切换能力。
依赖注入
主插件服务注册集中在 MyRevitPlugin/Extensions/ServiceCollectionExtensions.cs,Host.cs 只负责创建 Host 和日志:
builder.Services.AddRevitPluginServices(builder.Configuration);
基础设施实现集中在 Infrastructure,接口集中在 Infrastructure/Interfaces。后续新增业务服务时,默认实现放在 Services,接口放在 Services/Interfaces。
日志
Host 使用 LogProviderService 接入标准 Microsoft.Extensions.Logging:
builder.Logging.AddProvider(new LogProviderService(logDirectory));
业务代码继续注入 ILogger<T>,不直接依赖文件实现。日志按天写入共享 Data 目录的 Logs 子目录,并自动保留最近 14 个日志文件。
本地数据服务由 Data 项目提供统一入口:
services.AddLocalDataServices(configuration);
默认会注册 IFreeSql、IPluginSettingService 和 IDatabaseBackupService。模板默认只引用 SQLite Provider;如果后续要切换 MySQL、PostgreSQL 或 SQL Server,需要在 Data 项目增加对应的 FreeSql.Provider.* 包,并在 appsettings.json 中配置连接字符串。
本地数据库
默认配置在 appsettings.json:
{
"Database": {
"Provider": "Sqlite",
"FileName": "MyRevitPlugin.db",
"AutoSyncStructure": true,
"EnableSqlLogging": false
}
}
数据库默认创建在共享 Data 目录:
%APPDATA%\MyRevitPlugin\Data\MyRevitPlugin.db
MSI 安装器会把用户选择的数据目录写入 HKCU\Software\MyRevitPlugin\DataPath。插件启动时优先读取该注册表值;未安装或读不到注册表时回退到 %APPDATA%\MyRevitPlugin\Data。
Data 项目只保留模板级基础设施。业务实体、仓储和领域服务应继续放在 MyRevitPlugin.Data 内扩展,避免把数据库实现耦合进 Revit UI/Command 项目。
技术栈
| 配置 | Revit | TargetFramework | Revit API 包 |
|---|---|---|---|
Debug_R24 / Release_R24 |
2024 | net48 |
Nice3point.Revit.Api.RevitAPI/UI 2024.3.40 |
Debug_R25 / Release_R25 |
2025 | net8.0-windows |
Nice3point.Revit.Api.RevitAPI/UI 2025.4.50 |
Debug_R26 / Release_R26 |
2026 | net8.0-windows |
Nice3point.Revit.Api.RevitAPI/UI 2026.4.10 |
Debug_R27 / Release_R27 |
2027 | net10.0-windows |
Nice3point.Revit.Api.RevitAPI/UI 2027.1.0 |
通用依赖:
- CommunityToolkit.Mvvm 8.4.2
- Microsoft.Extensions.Hosting 9.0.4 for R24/R25/R26, 10.0.9 for R27
- FreeSql / FreeSql.Provider.Sqlite 3.2.833
- WixSharp / WixSharp.bin 1.26.0
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.