如何在 VSPackage 中管理撤销/重做堆栈?

3
我创建了一个 VSPackage,在 Visual Studio 代码窗口上下文菜单中提供某些功能。该操作包括几个代码编辑和一些其他操作。
问题是,每个代码编辑都会单独添加到撤消栈中。我想要将此操作作为一个原子单位处理,即按下 CTRL+Z 可以回滚所有代码编辑和其他操作(同时将该单位放置在重做栈的顶部)。
关于此主题的文档非常贫乏,我找到的唯一信息是关于 IOleParentUndo 单元的内容 - 但我无法成功地实现它。
我使用
IVsTextLines.GetUndoManager()

获取撤销管理器 - 这似乎是一个不错的开始。
3个回答

2
我将以下代码封装起来,以撤销一些操作。
public class VSUndo : IDisposable
{
    public static UndoContext undoContext;

    public static VSUndo StartUndo()
    {
        undoContext = ((DTE2)Package.GetGlobalService(typeof(DTE))).UndoContext; 
        undoContext.Open(Guid.NewGuid().ToString());
        // return new instance for calling dispose to close current undocontext
        return new VSUndo(); 
    }

    public void Dispose()
    {
        undoContext.Close();
    }

}

那么,你可以直接使用:

using (VSUndo.StartUndo())
{
   // couple of actions that may need to undo together
}

1

尝试使用类似这样的东西:

IDesignerHost host = ...;
DesignerTransaction transaction = host.CreateTransaction("Command Name");
try
{
  // Command Body
  TypeDescriptor.GetProperties(control)["Location"].SetValue(control, location);
  transaction.Commit();
}
catch
{
    transaction.Cancel();
    throw;
}

1
对不起,我不明白。什么是“控件”?什么是“位置”?这与撤销堆栈有什么关系? - cre8or
这是我在设计界面上执行某些操作时使用事务的方式。我获取主机,然后打开事务,在表面上编辑控件,最后提交事务。 - Iaroslav Shcherbyniuk
你可以将我的项目作为一个例子查看 http://sourceforge.net/projects/reportdesinerex/ - Iaroslav Shcherbyniuk

1

如果撤销操作是在文档级别上管理的,那么您可能需要使用RunningDocumentTable来枚举表格、获取它们的撤销管理器并从轨道上摧毁它们:

class NavigateListener: IVsRunningDocTableEvents3
{
    private HashSet<IVsTextView> views = new HashSet<IVsTextView>();
    private IVsRunningDocumentTable table;
    private uint cookie;
...

在外部,称之为注册调用 - 添加一个取消注册调用来取消建议的事件。

public void Register()
{
    table =(IVsRunningDocumentTable)    Package.GetGlobalService(typeof(SVsRunningDocumentTable));
    // Listen to show/hide events of docs to register activate/deactivate cursor  listeners.
    table.AdviseRunningDocTableEvents(this, out cookie);
}

运行表有很多事件,例如保存等,但您可以监听视图何时被注册:

public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
{
    IVsTextView view = VsShellUtilities.GetTextView(pFrame);
    if (view != null)
    {
        views.Add(view);
    }
}

您可能会遇到已保存更改但随后被关闭的文档问题...现在先将它们删除...

public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
    IVsTextView view = VsShellUtilities.GetTextView(pFrame);
    if (view != null)
    {
        views.Remove(view);
    }
    return VSConstants.S_OK;
}

然后你可以执行超级撤销核弹。我还没有测试这部分 - 你需要自己尝试。

private void NukeFromOrbit()
{
    foreach( var view in views )
    {
        IVsTextLines buffer;
        view.GetBuffer(out buffer);
        IOleUndoManager manager;
        buffer.GetUndoManager(out manager);
        IEnumOleUndoUnits units;
        manager.EnumUndoable(out units);
        uint fetched=0;
        var unitArray = new IOleUndoUnit[1];
        while( units.Next(1, unitArray , out fetched ) == VSConstants.S_OK)
        {
            unitArray[0].Do(manager);
        }
    }
}

我其实自己想出了这样的解决方案 - 但很高兴知道没有更好的,谢谢 :-) - cre8or

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