使用 PostSharp 的嵌套 undo/redo 操作

Nested undo/redo operations with PostSharp

我刚刚开始使用 PostSharp,目前使用的是所提供的 Undo/Redo 功能,但将来可能会使用更多。

我想知道是否有任何方法可以在我们的应用程序中实现一组嵌套操作。

所以我在主应用程序中有我的对象模型,然后我启动一个编辑器 window 来修改消息列表。我可以正确地让 PostSharp 监控我对每条消息所做的所有单独更改,并允许用户撤消这些更改。到目前为止一切顺利。

然后我想要的是允许用户退出编辑器,现在在撤消堆栈上有一个单一的操作 - "Undo Message Changes" 或类似的东西。这将涉及获取所有单独的更改,并在编辑器被解雇时将它们合并到一个操作中。 我目前正在研究 OperationScope 方法,但我不确定我的方向是否正确。当在一个范围内时,操作不会立即添加到撤消堆栈,它们只会在范围关闭时添加,这不是我想要的。

我已经包含了一个非常精简的测试代码示例,以及一些关于我在不同点上期望的评论。可能是我想要的东西是不可能的,但希望有人能有一些建议!

    private void btnShowEditor_Click(object sender, EventArgs e)
    {
        // Nothing on the undo stack at this point
        LaunchEditorWindow();

        // I would like one composite operation at this point that can undo all of the changes made within LaunchEditorWindow
    }

    // Imagine this code is inside an editor window and the changes are being done by the user
    private void LaunchEditorWindow()
    {
        _network.Messages.Add(new Message() { Name = "Message 1", BitLength = 1 });

        // I would expect there to be three undo operations available here
        System.Diagnostics.Debug.WriteLine(RecordingServices.DefaultRecorder.UndoOperations.Count);

        _network.Messages.Add(new Message() { Name = "Message 2", BitLength = 2 });

        // 6 operations now
        _network.Messages.Add(new Message() { Name = "Message 3", BitLength = 3 });

        // etc etc
        _network.Messages.Add(new Message() { Name = "Message 4", BitLength = 4 });

        System.Diagnostics.Debug.WriteLine(RecordingServices.DefaultRecorder.UndoOperations.Count);
    }

PostSharp undo/redo有一个嵌套操作的概念,但是这个概念没有暴露给public API:它在内部用于支持原子操作。不向用户公开树结构背后的基本原理是用户通常期望线性结构。

OperationScope 不允许您向用户公开层次结构。范围可以是分层的,但操作是自动的 "flattened",即新范围并不总是导致新操作(如果父操作存在,则重复使用,除非新范围是原子的)。

您可以调查还原点(参见 http://doc.postsharp.net/m_postsharp_patterns_recording_recorder_addrestorepoint_762235a2)。

看来您想要的是能够将从还原点到当前点的所有操作合并为给定名称的新操作。当前 API 不允许这样做。我添加了功能请求 http://postsharp.uservoice.com/forums/4949-feature-requests/suggestions/7776894-undo-redo-api-to-merge-operations,我鼓励您为它投票。

请注意,您可以通过在 Recorder.UndoOperations 之上创建自定义 "view model" 来自己实现该功能,这将使用对您有意义的聚合级别显示操作。这是一个明智的选择,因为它可以让您完全控制 undo/redo 系统的用户视图,这基本上就是您想要的。然而,这意味着您必须创建自己的 undo/redo 按钮。