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

时间:2012-05-07 19:58:28

标签: c# vsx vspackage

我创建了VSPackage,它在Visual Studio代码窗口上下文菜单中提供了某些功能。此操作包含多个代码编辑以及一些内容。

问题是,每个代码编辑都会分别添加到撤消堆栈中。我想要的是将这个动作作为一个原子单元处理,即按下CTRL + Z回滚所有代码编辑和其他所有内容(当然,加上单位置于重做堆栈之上)。

关于这个主题的文档非常差,我发现的唯一一件事就是IOleParentUndo单元 - 但我没有成功实现它。

我用

IVsTextLines.GetUndoManager()

获取撤消管理器 - 这似乎是一个良好的开端。

3 个答案:

答案 0 :(得分: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 :(得分: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);
        }
    }
}

答案 2 :(得分: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;
}