源码 | 使用AI基于AvaloniaUI MVVM 架构开发跨平台三维应用
摘要
本文介绍如何使用 AvaloniaUI 框架和 MVVM 架构模式开发现代化的跨平台三维 CAD 应用程序。通过 RapidCAX 项目实例,详细讲解视图切换、命令绑定、消息传递、AnyCAD三维渲染集成等核心技术的实现方法。本项目代码使用AI完成,代码完全开源[1]。

注: 为了让AI能够精确了解AnyCAD图形平台API,请在项目中引用专为AI设计的API文档API4AI[2]。
关键词:AvaloniaUI, MVVM, 三维 CAD, 跨平台,AnyCAD
1. 引言
随着工业软件国产化需求的日益增长,开发自主可控的 CAD/CAE 软件成为重要趋势。AvaloniaUI 作为一个强大的跨平台 UI 框架,结合 AnyCAD 三维图形平台,为开发 Windows、Linux、macOS 多平台支持的三维应用提供了理想的技术栈。
1.1 技术选型
-
• UI 框架: AvaloniaUI 11.x (跨平台 XAML 框架) -
• MVVM 库: CommunityToolkit.Mvvm (轻量级 MVVM 组件) -
• 三维引擎: AnyCAD Platform (专业 CAD 平台 SDK) -
• UI 主题: SukiUI 6.0 (现代化深色主题) -
• 开发语言: C# .NET 8.0
1.2 项目特点
RapidCAX 是一个典型的三维 CAD 应用框架,具备以下特点:
-
• 纯 MVVM 架构,View 与 ViewModel 完全解耦 -
• 多视图并行显示(二维草图 + 三维模型) -
• 全局共享三维渲染控件 -
• 支持文件打开、新建、保存等标准操作 -
• 左右分栏可调节布局 -
• 深色主题界面
2. 项目架构设计
2.1 整体架构图
┌─────────────────────────────────────────────┐
│MainWindow.axaml (View)│
│┌─────────────────────────────────────┐│
││MainViewModel(DataContext)││
││-ViewMenuItems││
││-SelectedView││
││-SwitchViewCommand││
││-OpenFileCommand││
││-NewFileCommand││
│└───────────┬─────────────────────────┘│
││DataBinding│
│┌───────────▼─────────────────────────┐│
││ViewsContainer││
││-HomeView││
││-SketchView││
││-PartView││
││-RenderControl(AnyCAD)││
│└─────────────────────────────────────┘│
└─────────────────────────────────────────────┘
2.2 目录结构
src/App/
├──ViewModels/
│├──MainViewModel.cs # 主窗口 ViewModel
│├──ViewItemViewModel.cs # 视图项基类
│├──HomeViewModel.cs # 主页视图 ViewModel
│├──SketchViewModel.cs # 二维视图 ViewModel
│├──PartViewModel.cs # 三维视图 ViewModel
│├──BomViewModel.cs # BOM 视图 ViewModel
│└──ComponentsViewModel.cs # 组件视图 ViewModel
├──Views/
│├──HomeView.axaml(.cs)# 主页视图
│├──SketchView.axaml(.cs)# 二维草图视图
│├──PartView.axaml(.cs)# 三维零件视图
│└──...
├──Converters/
│├──IsSameConverter.cs # 相等判断转换器
│└──IsSameMultiConverter.cs # 多值相等判断转换器
├──MainWindow.axaml(.cs)# 主窗口
└──App.axaml(.cs)# 应用程序入口
3. 核心功能实现
3.1 ViewModel 继承体系
为了保持代码的可维护性和扩展性,我们建立了清晰的 ViewModel 继承层次:
基类设计 (ViewItemViewModel.cs):
usingCommunityToolkit.Mvvm.ComponentModel;
namespaceRapidCAX.App.ViewModels
{
///<summary>
/// 视图项 ViewModel(包含工具栏按钮和视图内容信息)
///</summary>
publicpartialclassViewItemViewModel:ObservableObject
{
[ObservableProperty]
privatestring _id;
[ObservableProperty]
privatestring _displayName;
[ObservableProperty]
privatestring _iconPath;
[ObservableProperty]
privatestring _toolTip;
[ObservableProperty]
privateobject _content;
publicViewItemViewModel(string id,string displayName,string iconPath,
string toolTip,object content)
{
Id= id;
DisplayName= displayName;
IconPath= iconPath;
ToolTip= toolTip;
Content= content;
}
}
}
派生示例 (PartViewModel.cs):
usingCommunityToolkit.Mvvm.ComponentModel;
namespaceRapidCAX.App.ViewModels
{
///<summary>
/// 三维设计视图模型
///</summary>
publicpartialclassPartViewModel:ViewItemViewModel
{
publicPartViewModel()
:base("3D","三维",
"M21,16.5C21,16.88 20.79,17.21...",// SVG 路径数据
"三维设计",
newViews.PartView())
{
}
}
}
优势分析:
-
• ✅ 职责单一:每个 ViewModel 只负责自己的视图 -
• ✅ 易于扩展:添加新视图只需创建新的 ViewModel 类 -
• ✅ 代码清晰:MainViewModel 不再包含冗长的初始化代码 -
• ✅ 便于维护:每个视图的逻辑在独立文件中
3.2 视图切换机制
3.2.1 XAML 绑定设计
<!-- 工具栏区域 -->
<ItemsControlItemsSource="{Binding ViewMenuItems}">
<ItemsControl.ItemTemplate>
<DataTemplateDataType="vm:ViewItemViewModel">
<ButtonClasses="ToolButton"
Command="{Binding $parent[ItemsControl].DataContext.SwitchViewCommand}"
CommandParameter="{Binding Id}"
ToolTip.Tip="{Binding ToolTip}">
<StackPanelOrientation="Vertical"HorizontalAlignment="Center"Spacing="4">
<PathIconData="{Binding IconPath}"Width="20"Height="20"/>
<TextBlockText="{Binding DisplayName}"FontSize="11"/>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- 视图内容区域 -->
<ItemsControlx:Name="viewsContainer"ItemsSource="{Binding ViewMenuItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControlContent="{Binding Content}">
<ContentControl.IsVisible>
<MultiBindingConverter="{x:Static converters:IsSameMultiConverter.Instance}">
<BindingPath="$parent[ItemsControl].DataContext.SelectedView"/>
<BindingPath="Id"/>
</MultiBinding>
</ContentControl.IsVisible>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
3.2.2 相等判断转换器
publicclassIsSameMultiConverter:IMultiValueConverter
{
publicstaticIsSameMultiConverterInstance{get;}=new();
publicobject?Convert(IList<object?> values,Type targetType,
object? parameter,CultureInfo culture)
{
if(values.Count>=2&& values[0]!=null&& values[1]!=null)
{
return values[0].Equals(values[1]);
}
returnfalse;
}
}
工作原理:
-
1. 用户点击工具栏按钮 → 触发 SwitchViewCommand -
2. ViewModel 更新 SelectedView属性 -
3. IsSameMultiConverter比较SelectedView与各视图的Id -
4. 匹配的视图设置 IsVisible = true,其他视图隐藏 -
5. 所有视图常驻内存,保持状态
3.3 命令与消息系统
3.3.1 命令定义
publicpartialclassMainViewModel:ObservableObject
{
[RelayCommand]
publicvoidSwitchView(string viewName)
{
SelectedView= viewName;
// 发送视图切换消息
WeakReferenceMessenger.Default.Send(newViewSwitchedMessage(viewName));
}
[RelayCommand]
privatevoidOpenFile()
{
// 发送消息请求打开文件,由 MainWindow 处理实际的对话框
WeakReferenceMessenger.Default.Send(newOpenFileRequestMessage());
}
[RelayCommand]
privatevoidNewFile()
{
// 发送消息请求清空视图
WeakReferenceMessenger.Default.Send(newClearViewerRequestMessage());
AnyCAD.Platform.Application.Instance().ExecuteCommand("New");
}
}
3.3.2 消息接收处理
publicpartialclassMainWindow:Window,
IRecipient<OpenFileRequestMessage>,
IRecipient<ClearViewerRequestMessage>
{
publicMainWindow()
{
InitializeComponent();
this.Loaded+=OnLoaded;
// 注册消息接收器
WeakReferenceMessenger.Default.Register<OpenFileRequestMessage>(this);
WeakReferenceMessenger.Default.Register<ClearViewerRequestMessage>(this);
}
publicasyncvoidReceive(OpenFileRequestMessage message)
{
var files =awaitthis.StorageProvider.OpenFilePickerAsync(
newFilePickerOpenOptions
{
Title="打开 ACAD 文件",
AllowMultiple=false,
FileTypeFilter=new[]
{
newFilePickerFileType("ACAD 文件")
{Patterns=new[]{"*.acad"}},
newFilePickerFileType("所有文件")
{Patterns=new[]{"*.*"}}
}
});
if(files.Count>0)
{
var filePath = files[0].Path.LocalPath;
var doc =DocumentIO.Load(filePath);
if(doc !=null)
{
_RenderControl.ClearAll();
Application.Instance().ShowDocument(doc);
_RenderControl.RequestDraw(EnumUpdateFlags.ZoomToFit);
}
}
}
publicvoidReceive(ClearViewerRequestMessage message)
{
_RenderControl.ClearAll();
}
}
消息流程:
用户操作→MenuItem.Command→ViewModel.Command
→WeakReferenceMessenger.Send(Message)
→MainWindow.Receive(Message)→执行 UI 操作
3.4 三维渲染集成
3.4.1 RenderControl 全局共享
<!-- 右侧固定的三维渲染窗口 -->
<BorderGrid.Column="2"Margin="0">
<anycad:RenderControlx:Name="_RenderControl"
ViewerReady="OnViewerReady"/>
</Border>
privatevoidOnViewerReady()
{
// 设置活动查看器
AnyCAD.Platform.Application.Instance().SetActiveViewer(_RenderControl);
}
3.4.2 关键特性
-
• 单例共享: RenderControl作为单例在整个应用中共享 -
• 原生窗口: 基于 NativeWindowHandle 实现,避免多父容器问题 -
• 状态保持: 视图切换时三维场景状态自动保持 -
• 多视口支持: 可同时显示多个三维视口 
3.5 布局系统设计
3.5.1 三层垂直布局
<GridRowDefinitions="Auto, *, Auto">
<!-- 顶部菜单栏 -->
<MenuGrid.Row="0">
<MenuItemHeader="文件">
<MenuItemHeader="新建"Command="{Binding NewFileCommand}"/>
<MenuItemHeader="打开"Command="{Binding OpenFileCommand}"/>
<Separator/>
<MenuItemHeader="退出"/>
</MenuItem>
</Menu>
<!-- 中间工作区 -->
<GridGrid.Row="1"ColumnDefinitions="Auto, 3, *">
<!-- 工具栏 + 视图容器 + 三维渲染 -->
</Grid>
<!-- 底部状态栏 -->
<BorderGrid.Row="2"Background="#2D2D30"Padding="10,5">
<TextBlockText="就绪"Foreground="#CCCCCC"/>
</Border>
</Grid>
3.5.2 左右分栏设计
<GridColumnDefinitions="250, 3, *">
<!-- 左侧:可切换的视图区域 (250px) -->
<BorderGrid.Column="0">
<ItemsControlx:Name="viewsContainer"/>
</Border>
<!-- 分隔条 (可拖动) -->
<GridSplitterGrid.Column="1"
ResizeDirection="Columns"
Width="1"
Background="#3E3E42"/>
<!-- 右侧:固定的三维渲染窗口 (自适应) -->
<BorderGrid.Column="2">
<anycad:RenderControl/>
</Border>
</Grid>
4. 关键技术要点
4.1 纯 MVVM 架构实践
核心原则:
-
1. ViewModel 不直接引用 View 的具体类型 -
2. 所有交互通过命令和消息实现 -
3. View 只负责 UI 呈现和用户输入 -
4. ViewModel 包含所有业务逻辑
实现效果:
// ✅ 正确:ViewModel 发送消息,不关心谁接收
WeakReferenceMessenger.Default.Send(newClearViewerRequestMessage());
// ❌ 错误:ViewModel 直接操作 View
// mainWindow._RenderControl.ClearAll(); // 不要这样做!
4.2 视图生命周期管理
常驻内存策略:
-
• 所有视图在初始化时一次性创建 -
• 通过 IsVisible控制显示/隐藏 -
• 切换视图时保持状态(如缩放比例、选择集)
privatevoidInitializeViewMenuItems()
{
// 所有视图在启动时创建并保持在内存中
ViewMenuItems.Add(newHomeViewModel());
ViewMenuItems.Add(newSketchViewModel());
ViewMenuItems.Add(newPartViewModel());
ViewMenuItems.Add(newBomViewModel());
ViewMenuItems.Add(newComponentsViewModel());
}
4.3 深色主题适配
SukiUI 主题配置:
<suki:SukiWindowxmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
Background="#1F1F1F">
<!-- 自定义颜色资源 -->
<suki:SukiWindow.Resources>
<SolidColorBrushx:Key="SukiPrimaryColor"Color="#007ACC"/>
<SolidColorBrushx:Key="SukiAccentColor"Color="#FF9800"/>
</suki:SukiWindow.Resources>
</suki:SukiWindow>
样式定义:
<StyleSelector="Button.ToolButton">
<SetterProperty="Background"Value="Transparent"/>
<SetterProperty="Foreground"Value="#CCCCCC"/>
<SetterProperty="Cursor"Value="Hand"/>
<StyleSelector="^:pointerover">
<SetterProperty="Background"Value="#3E3E42"/>
<SetterProperty="Foreground"Value="#FFFFFF"/>
</Style>
<StyleSelector="^:checked">
<SetterProperty="Background"Value="#007ACC"/>
</Style>
</Style>
4.4 性能优化
优化措施:
-
1. 视图缓存: 避免重复创建 UserControl -
2. 延迟加载: 大型数据按需加载 -
3. 虚拟化列表: ItemsControl 启用虚拟化 -
4. 异步操作: 文件操作使用 async/await
publicasyncvoidReceive(OpenFileRequestMessage message)
{
// 异步文件操作,避免阻塞 UI
var files =awaitthis.StorageProvider.OpenFilePickerAsync(options);
// ...
}
5. 常见问题与解决方案
5.1 控件多父容器问题
问题描述:
InvalidOperationException:'RenderControl'is already a child of another visual.
解决方案:
-
• 使用 NativeControlHost或 Border 包裹 -
• 确保控件同时只有一个父容器 -
• 通过可见性控制而非从视觉树移除
5.2 消息注册时机
问题描述: 消息已发送但未接收到
正确做法:
publicMainWindow()
{
InitializeComponent();
// ✅ 在构造函数中立即注册
WeakReferenceMessenger.Default.Register<OpenFileRequestMessage>(this);
}
5.3 DataContext 生命周期
注意事项:
-
• Window 的 DataContext 应在 XAML 中设置 -
• 不要在 code-behind 中重复设置 -
• 确保 ViewModel 先于 View 初始化
6. 扩展与进阶
6.1 添加新视图
步骤:
-
1. 创建 View: SketchView.axaml(.cs) -
2. 创建 ViewModel: SketchViewModel.cs -
3. 在 MainViewModel.InitializeViewMenuItems()中注册
ViewMenuItems.Add(newSketchViewModel());
6.2 添加工具栏功能
示例 – 导出功能:
[RelayCommand]
privateasyncTaskExport()
{
WeakReferenceMessenger.Default.Send(newExportRequestMessage());
}
// MainWindow 中处理
publicvoidReceive(ExportRequestMessage message)
{
var file =awaitthis.StorageProvider.SaveFilePickerAsync(/* ... */);
// 执行导出逻辑
}
6.3 插件化架构
设计思路:
-
• 定义插件接口 IPlugin -
• 使用 MEF 或自定义容器加载插件 -
• 通过消息机制与主程序通信
7. 总结与展望
7.1 技术优势
-
1. 跨平台能力: Windows、Linux、macOS 一套代码 -
2. 现代化 UI: Fluent Design、Material Design 支持 -
3. 高性能: 硬件加速、虚拟化、异步编程 -
4. 易维护: MVVM 架构、依赖注入、单元测试友好
7.2 未来方向
-
• AI 辅助设计 -

附录
A. 开发环境配置
# .NET SDK 8.0+
dotnet --version
# 安装 Avalonia 模板
dotnet new install Avalonia.Templates
# 克隆项目
git clone https://github.com/anycad/rapidcax.avalonia.git
# 构建运行
cd rapidcax.avalonia/src/App
dotnet run
B. 核心 NuGet 包
<PackageReferenceInclude="Avalonia"Version="11.1.0"/>
<PackageReferenceInclude="CommunityToolkit.Mvvm"Version="8.2.2"/>
<PackageReferenceInclude="SukiUI"Version="6.0.0"/>
<PackageReferenceInclude="AnyCAD.Avalonia.NET"Version="2026.x.xx"/>
C. 参考资源
-
• AvaloniaUI 官方文档:https://docs.avaloniaui.net/ -
• AnyCAD 开发文档:http://www.anycad.cn/ -
• RapidCAX 源码:https://gitee.com/rapidcax/rapidcax.avalonia -
• API4AI: https://gitee.com/anycad/anycad.faq.git
引用链接
[1] 代码完全开源: https://gitee.com/rapidcax/rapidcax.avalonia[2] API4AI: gitee.com/anycad/anycad.faq.git
夜雨聆风