【工业软件开发学习——AnyCAD篇】事务(Transaction)与撤销(Undo)、重做(Redo)机制

引子 前一阵基于AnyCAD做了些小项目,发现SDK中不少与文档(Document)、元素构件(Element)相关的概念和Revit等BIM软件有一定相似度,这在进行业务开发时有很大用处,但不知为何目前官方在这方面的文档比较少,也未提供太多具体示例,

引子

前一阵基于AnyCAD做了些小项目,发现SDK中不少与文档(Document)、元素构件(Element)相关的概念和Revit等BIM软件有一定相似度,这在进行业务开发时有很大用处,但不知为何目前官方在这方面的文档比较少,也未提供太多具体示例,所以准备选取一些Highlight简单写些小教程。工业软件SDK或二次开发接口大多使用相似的架构和理念,一方面借此作为自己学习和开发的基础,另一方面也希望可以帮助到从事相关工作的朋友。

概述

AnyCAD图形平台主要提供一系列造型、显示、以及一些工业领域应用的脚手架,gitee上有不少官方提供的示例仓库:

  • rapid.net.starter
  • anycad.rapid.net.sample
  • RapidCAX

还不熟悉此平台的朋友可以先从官网和上面的例子入门,这里不作过多展开。

在大多例子中,AnyCAD创建和操作的对象都仅仅用于显示,但复杂的CAD软件往往需要具备数据管理功能,所有针对数据对象的增删查改都应可回退或重做(Undo/Redo),AnyCAD也提供了这些基础功能,在官方开发指南数据管理这一节介绍了基本概念和用法,本文fork了rapid.net.starter这一仓库进行了一些修改,需要参考的朋友可以把代码pull到本地直接运行(安装相关依赖的前提下,具体依赖请参考官方提供的教程)。

准备工作

AnyCAD提供的应用框架AppFramework能够让开发者以MVVM的模式快速搭建应用,我们这里也借助它来完成应用与文档的初始化;安装nuget包后新建一个MainViewModelImpl.cs文件,创建一个从AnyCAD.NX.ViewModel.WpfMainViewModel继承的类即可:

public partial class MainViewModelImpl : WpfMainViewModel
{
    public MainViewModelImpl(IRenderView viewer) 
        : base(viewer)
    {

    }
}

接着在MainWindow的构造函数中实例化MainViewModelImpl:

public MainWindow()
{
    InitializeComponent();
    var viewModel = new MainViewModelImpl(mRenderCtrl);
    this.DataContext = viewModel;
}

并在已经注册的ViewerReady事件函数中初始化viewModel:

private void mRenderCtrl_ViewerReady()
{
    // 初始化
    ViewModel.Initialize(mRenderCtrl);
}

此时编译运行程序将会得到一个已初始化文档对象的窗口:

image-20240106165200909

在事务中创建对象

参照开发指南里的示例代码,我们增加一个菜单项用于创建包含圆柱体形状的ShapeElement,并将这部分代码包含在UndoTransaction的Start与Commit之间:

// MainWindow.xaml

<Metro:MetroWindow.RightWindowCommands>
    <Metro:WindowCommands ShowSeparators="False">
        <Menu Margin="50,2,0,0" Background="#00000000">
            <MenuItem  Header="创建" Background="#00000000">
                <MenuItem  Header="圆柱体" Command="{Binding NewCylinderCommand}"/>
            </MenuItem>
        </Menu>
    </Metro:WindowCommands>
</Metro:MetroWindow.RightWindowCommands>
    

// MainViewModelImpl.cs
[RelayCommand]
void OnNewCylinder() // 该方法与xaml中的创建圆柱体命令绑定
{
    UndoTransaction undo = new(Document); // 实例化UndoTransaction对象
    undo.Start("create.cylinder"); // 开启事务
    
    var shape = ShapeBuilder.MakeCylinder(GP.XOY(), 10, 20, 0); // 创建圆柱体shape
    var shapeElement = ShapeElement.Create(Document); // 在文档中创建ShapeElement对象
    shapeElement.SetShape(shape); // 将构造好的shape赋予ShapeElement对象
    
    undo.Commit(); // 提交事务
}

运行后通过菜单项便可创建圆柱体:

image-20240106170040096

Undo/Redo操作

目前为止和rapid.net.starter原本所展示的内容似乎并无区别,但实际上新建圆柱体这一操作已经能够进行回退操作了,由于SDK并没有自带回退与重做键盘事件的绑定,我们需要在MainWindow.xaml自行绑定:

<Metro:MetroWindow.InputBindings>
    <KeyBinding
        Key="Z"
        Command="{Binding UndoCommand}"
        Modifiers="Control" />
    <KeyBinding
        Key="Y"
        Command="{Binding RedoCommand}"
        Modifiers="Control" />
</Metro:MetroWindow.InputBindings>

或者也可以增加菜单项来执行这两个命令:

<MenuItem  Header="事务" Background="#00000000">
    <MenuItem Header="回退" Command="{Binding ExecuteSystemCommand}"  CommandParameter="Undo"/>
    <MenuItem Header="重做" Command="{Binding ExecuteSystemCommand}"  CommandParameter="Redo"/>
</MenuItem>

那么这时我们再创建圆柱体之后就可以通过快捷键或菜单项进行Undo/Redo操作了:

img

在事务中编辑对象

同时,后续对已创建对象的编辑也同样支持在事务中进行,以进行Undo/Redo操作,AnyCAD也提供了移动/旋转物体的小控件,调用"MoveInstance/RotateInstance"命令即可唤起:

<MenuItem  Header="编辑" Background="#00000000">
    <MenuItem Header="移动" Command="{Binding ExecuteSystemCommand}" CommandParameter="MoveInstance"></MenuItem>
    <MenuItem Header="旋转" Command="{Binding ExecuteSystemCommand}" CommandParameter="RotateInstance"></MenuItem>
</MenuItem>

并且,通过这两个小控件进行的编辑操作已经被包含在了事务中,不再需要开发者去操作UndoTransaction对象(不过截止文章撰写时,这里需要两次Undo才能回到编辑前的状态,已report issue给平台,想必后续会修复这个问题):

img

总结

数据管理是开发复杂CAD应用的重要功能,AnyCAD帮助开发者实现了一部分核心内容,这大大提升了业务实现的效率。后续我将结合自己的经历撰写更多工业软件开发的小文章,欢迎大家阅读与指正。

知秋君
上一篇 2024-07-13 13:48
下一篇 2024-07-13 13:12

相关推荐