如何将操作推入Excel撤销堆栈?

11
我正在构建一个VSTO Excel插件,用于操作多个单元格的值。我希望通过标准的Excel功能,允许用户撤销和重做插件所创建的更改。我倾向于避免使用VBA。
这是否可能?如果是,怎么做?
另一个问题:是否可能检查现有的撤消/重做堆栈?
4个回答

5
您没有提到希望使用哪个版本的Excel/.NET/VSTO运行时,但这并不重要:)
自定义撤销功能需要注入VBA才能实现,您需要设置一个撤销方法来回滚您的操作,可以通过补偿(即反向操作)或恢复保存状态来实现。

4

我知道你想避免使用VBA,但正如其他人所提到的那样,这是不可能实现撤销功能的。

以下是一个VBA示例,它将现有选择保留为自定义数据类型,以便稍后可以撤消。

Option Explicit
'Stores info about current selection'
Public OldWorkbook As Workbook
Public OldSheet As Worksheet
Public OldSelection() As SaveRange

'Custom data type for undoing'
Type SaveRange
    Value As Variant
    Address As String
End Type

Public Sub YourSubRoutine()
    'A simple subroutine that acts on a Range selection'
    Dim UserRange As Range

    Set UserRange = Selection


    If UserRange Is Nothing Then
        Exit Sub
    End If

'## The next block of statements '
'## Save the current values for undoing '
    Set OldWorkbook = ActiveWorkbook
    Set OldSheet = ActiveSheet

    Application.ScreenUpdating = False

    '## Code to manipulate the Selection range '
    '## Code to manipulate the Selection range '
    '## Code to manipulate the Selection range '

    '## Specify the Undo Sub '
    Application.OnUndo "Undo the YourSubRoutine macro", "UndoYourSubRoutine"

End Sub
Public Sub UndoYourSubRoutine()

Dim i As Integer

'   Undoes the effect of the YourSubRoutine '

'   Tell user if a problem occurs '
    On Error GoTo Problem

    Application.ScreenUpdating = False

'##  Make sure the correct workbook and sheet are active '
    OldWorkbook.Activate
    OldSheet.Activate

'## Restore the saved information '
    For i = 1 To UBound(OldSelection)
        Range(OldSelection(i).Address).Value = OldSelection(i).Value
    Next i
    Exit Sub

'   Error handler'
Problem:
    MsgBox "Can't undo"

End Sub

我能否从C#生成和注入VBA,并使用某种唯一标识符将ID存储在我的应用程序中,映射到状态转换,并使子程序本身只是对C#的回调,当调用时,反转变换? - Richard Pianka
抱歉,我无法编辑我的评论... 可以尝试将这个链接https://dev59.com/Lm_Xa4cB1Zd3GeqPyC2S 和这个链接http://msdn.microsoft.com/en-us/library/vstudio/bb608614(v=vs.100).aspx 结合起来使用,也许可以解决问题? - Richard Pianka
我在这方面有点不熟悉,但是浏览了一下似乎可以结合这些方法。最终,您需要反转这些过程或将受影响的数据副本存储在内存中,并在“撤消”调用时恢复到该状态。似乎两者都可以在C#中完成--但是将它们与Excel的标准界面(例如,Ctrl+Z快捷键进行撤消等)绑定需要一些代码注入--即使只是一个VBA子程序来调用外部对象(C#)执行“撤消”。 - David Zemens
6
一个替代VBA的OnUndo例程的选择是将你的C#宏放在Excel-DNA插件中。Excel-DNA使用本地Excel C API注册函数和过程,因此您不会受到VSTO的限制。 - Govert

0

您可以使用以下方法将操作添加到堆栈中:

Application.OnUndo text, procedureName

其中Text是撤销命令(编辑菜单)中显示的文本,procedureName是您其中一个子程序的名称。您还可以通过编程方式撤销操作,方法如下:

Application.Undo

我觉得可能无法访问现有的撤销操作;至少我从未听说过。可能有一个在线可访问的库可以实现这一点。
希望能对你有所帮助。

那意味着我需要使用VBA吗? - Richard Pianka

-1

我最终选择的解决方案如下:

  • 保存剪贴板状态的副本
  • 清除剪贴板
  • 生成所需数据(以制表符/换行符分隔的格式),将其推送到剪贴板
  • 模拟在 Excel 中进行 Ctrl+V 操作
  • 清除剪贴板
  • 恢复原始剪贴板状态

显然,这仅适用于单元格操作,因此不能将任意操作/回调推送到撤消堆栈中。此外,我很明显违反了有关 Windows 剪贴板使用的原则,但如果 Microsoft 为这些事情公开了更好的 API(提示),我就不必这样做。

此外,我没有选择我在 David Zemens 的答案第一条评论中描述的解决方案,因为我在我们的环境中遇到了一些安全问题(即,在工作簿中注入 VBA 代码是不允许的)。

总之,感谢大家!


劫持剪贴板是不可行的,因为你可能会覆盖用户的内容,如果这样做,他们会非常生气。 - Leo Gurdian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接