在 Visual Studio 实验实例中,我需要删除解决方案资源管理器中“剪切”和“删除”的键盘快捷键

问题描述 投票:0回答:1

我正在尝试在解决方案资源管理器的实验实例中删除“剪切”和“删除”的键盘快捷键。 下面的代码在全球范围内运行(对于整个视觉工作室来说是有限制的)。 我需要将其限制为解决方案资源管理器,当我们单击任何特定文件时,剪切和删除键盘快捷键应该不起作用。

这是我尝试过的代码片段:

DTE dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE));
Command command = dte.Commands.Item("Edit.Cut", -1);

if (command.Bindings != null)
{
   command.Bindings = new object[];
}
c# visual-studio keyboard-shortcuts vsix
1个回答
0
投票

这非常棘手,所以请耐心等待。

引自将 Visual Studio 编辑器与投影缓冲区分开

Visual Studio 使用“命令链设计模式”来路由命令。本质上,链表是由对监听命令感兴趣的不同组件(全部继承自IOleCommandTarget

)创建的。基本命令包括
箭头按键、CTRL+Z退格。有关许多可能命令的示例,请参阅 VSConstants.VSStd97CmdID

有了这些信息,我们可以想象类似获取解决方案资源管理器窗口的

WindowPane

(它实现 
IOleCommandTarget
 接口)并尝试更改其命令目标。经过一番搜索,我得到了这个:
// get the solution explorer window, note that you need to make sure it is loaded at the time var solutionExplorerWindow = await VS.Windows.GetSolutionExplorerWindowAsync(); solutionExplorerWindow.Frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out object property); // note that it is not actually `ToolWindowPane`, but an internal derived class ToolWindowPane solutionExplorerPane = property as ToolWindowPane;

之后,我设置了一个断点来检查它有哪些字段,发现它有一个 
IOleCommandTarget _commandTarget

字段,这是非常不言自明的。现在我们只需要将其更改为我们自己的

IOleCommandTarget
。棘手的是,字段是私有的,而类是内部的。所以我们需要做一些反射魔法:
FieldInfo commandTargetField = solutionExplorerPane.GetType().BaseType.GetField("_commandTarget", BindingFlags.Instance | BindingFlags.NonPublic); // get the original command target var originalCommandTarget = commandTargetField.GetValue(solutionExplorerPane) as IOleCommandTarget; // create out command handler, with the orignal command target CommandHandler commandHandler = new(originalCommandTarget); // "hook" its command target commandTargetField.SetValue(solutionExplorerPane, commandHandler);

在上面的代码片段中,我们得到它的 
_commandTarget

,并构造我们自己的

CommandHandler
来覆盖原来的,基本上是一个钩子。以下是
CommandHandler
的实施方式:
internal class CommandHandler : IOleCommandTarget
{
    private readonly IOleCommandTarget _commandTarget;

    internal CommandHandler(IOleCommandTarget commandTarget)
    {
        _commandTarget = commandTarget;
    }

    public DesignerVerbCollection Verbs => throw new NotImplementedException();

    // Disable this warning because this will always be executed on the main thread
#pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread

    // This method will be called to check if we can handle the command or not
    public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
    {
        // do this if you want to hide the button on the "Edit" tool bar
        if (pguidCmdGroup == VSConstants.CMDSETID.StandardCommandSet97_guid)
        {
            for (uint i = 0; i < prgCmds.Length; i++)
            {
                if (prgCmds[i].cmdID == (uint)VSConstants.VSStd97CmdID.Cut ||
                    prgCmds[i].cmdID == (uint)VSConstants.VSStd97CmdID.Delete)
                {
                    prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_INVISIBLE | OLECMDF.OLECMDF_INVISIBLE);
                }
            }
        }
        return _commandTarget.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
    }

    // This method will be called to actually execute the command
    public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
    {
        if (pguidCmdGroup == VSConstants.CMDSETID.StandardCommandSet97_guid)
        {
            if (nCmdID == (uint)VSConstants.VSStd97CmdID.Cut ||
                nCmdID == (uint)VSConstants.VSStd97CmdID.Delete)
            {
                return VSConstants.S_OK;
            }
        }
        return _commandTarget.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
    }
#pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread
}

请注意,您可能还想检查其他命令集,例如 

VSConstants.VSStd2KCmdID

© www.soinside.com 2019 - 2024. All rights reserved.