可以在编辑器扩展中响应快捷键。 以下演练演示如何使用快捷键向文本视图添加视图装饰。 本演练基于视区装饰编辑器模板,它允许你使用 + 字符添加装饰。
创建托管扩展性框架 (MEF) 项目
- 创建 C# VSIX 项目。 (在 “新建项目 ”对话框,选择 Visual C# /扩展性,然后选择 VSIX Project。)将解决方案 - KeyBindingTest命名为 。
- 向项目添加编辑器文本装饰项模板并将其命名 - KeyBindingTest。 有关详细信息,请参阅 使用编辑器项模板创建扩展。
- 添加以下引用并将 CopyLocal 设置为 - false:- Microsoft.VisualStudio.Editor - Microsoft.VisualStudio.OLE.Interop - Microsoft.VisualStudio.Shell.14.0 - Microsoft.VisualStudio.TextManager.Interop - 在 KeyBindingTest 类文件中,将类名更改为 PurpleCornerBox。 使用左侧边距中显示的灯泡进行其他适当的更改。 在构造函数中,将装饰层的名称从 KeyBindingTest 更改为 PurpleCornerBox: 
this.layer = view.GetAdornmentLayer("PurpleCornerBox");
在 KeyBindingTestTextViewCreationListener.cs 类文件中,将 AdornmentLayer 的名称从 KeyBindingTest 更改为 PurpleCornerBox:
[Export(typeof(AdornmentLayerDefinition))]
[Name("PurpleCornerBox")]
[Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
public AdornmentLayerDefinition editorAdornmentLayer;
处理 TYPECHAR 命令
在 Visual Studio 2017 版本 15.6 之前,在编辑器扩展中处理命令的唯一 IOleCommandTarget 方法是实现基于命令筛选器。 Visual Studio 2017 版本 15.6 引入了基于编辑器命令处理程序的新式简化方法。 接下来的两个部分演示了如何使用旧方法和新式方法处理命令。
定义命令筛选器(Visual Studio 2017 版本 15.6 之前)
命令筛选器是一种实现 IOleCommandTarget,它通过实例化装饰来处理命令。
- 添加一个类文件并将其命名为 - KeyBindingCommandFilter。
- 添加以下 using 指令。 - using System; using System.Runtime.InteropServices; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Text.Editor;
- 名为 KeyBindingCommandFilter 的类应继承自 IOleCommandTarget. - internal class KeyBindingCommandFilter : IOleCommandTarget
- 为文本视图添加专用字段、命令链中的下一个命令,以及用于表示是否已添加命令筛选器的标志。 - private IWpfTextView m_textView; internal IOleCommandTarget m_nextTarget; internal bool m_added; internal bool m_adorned;
- 添加设置文本视图的构造函数。 - public KeyBindingCommandFilter(IWpfTextView textView) { m_textView = textView; m_adorned = false; }
- QueryStatus()按如下所示实现该方法。- int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { return m_nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); }
- 实现该方法 - Exec(),以便在键入加号 (+) 字符时向视图添加紫色框。- int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (m_adorned == false) { char typedChar = char.MinValue; if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); if (typedChar.Equals('+')) { new PurpleCornerBox(m_textView); m_adorned = true; } } } return m_nextTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); }
添加命令筛选器(Visual Studio 2017 版本 15.6 之前)
装饰提供程序必须将命令筛选器添加到文本视图。 在此示例中,提供程序实现 IVsTextViewCreationListener 侦听文本视图创建事件。 此装饰提供程序还导出装饰层,该层定义装饰的 Z 顺序。
- 在 KeyBindingTestTextViewCreationListener 文件中,添加以下 using 指令: - using System; using System.Collections.Generic; using System.ComponentModel.Composition; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop;
- 若要获取文本视图适配器,必须导入 。IVsEditorAdaptersFactoryService - [Import(typeof(IVsEditorAdaptersFactoryService))] internal IVsEditorAdaptersFactoryService editorFactory = null;
- TextViewCreated更改方法,使其添加 - KeyBindingCommandFilter.- public void TextViewCreated(IWpfTextView textView) { AddCommandFilter(textView, new KeyBindingCommandFilter(textView)); }
- 处理程序 - AddCommandFilter获取文本视图适配器并添加命令筛选器。- void AddCommandFilter(IWpfTextView textView, KeyBindingCommandFilter commandFilter) { if (commandFilter.m_added == false) { //get the view adapter from the editor factory IOleCommandTarget next; IVsTextView view = editorFactory.GetViewAdapter(textView); int hr = view.AddCommandFilter(commandFilter, out next); if (hr == VSConstants.S_OK) { commandFilter.m_added = true; //you'll need the next target for Exec and QueryStatus if (next != null) commandFilter.m_nextTarget = next; } } }
实现命令处理程序(从 Visual Studio 2017 版本 15.6 开始)
首先,更新项目的 Nuget 引用以引用最新的编辑器 API:
- 右键单击项目,然后选择“ 管理 Nuget 包”。 
- 在 Nuget 程序包管理器中,选择“汇报”选项卡,选择“选择所有包”检查框,然后选择“更新”。 
命令处理程序是一种实现 ICommandHandler<T>,它通过实例化装饰来处理命令。
- 添加一个类文件并将其命名为 - KeyBindingCommandHandler。
- 添加以下 using 指令。 - using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Utilities; using System.ComponentModel.Composition;
- 名为 KeyBindingCommandHandler 的类应继承, - ICommandHandler<TypeCharCommandArgs>并将其导出为 ICommandHandler:- [Export(typeof(ICommandHandler))] [ContentType("text")] [Name("KeyBindingTest")] internal class KeyBindingCommandHandler : ICommandHandler<TypeCharCommandArgs>
- 添加命令处理程序的显示名称: - public string DisplayName => "KeyBindingTest";
- GetCommandState()按如下所示实现该方法。 由于此命令处理程序处理核心编辑器 TYPECHAR 命令,因此它可以将启用命令委托给核心编辑器。- public CommandState GetCommandState(TypeCharCommandArgs args) { return CommandState.Unspecified; }
- 实现该方法 - ExecuteCommand(),以便在键入加号 (+) 字符时向视图添加紫色框。- public bool ExecuteCommand(TypeCharCommandArgs args, CommandExecutionContext executionContext) { if (args.TypedChar == '+') { bool alreadyAdorned = args.TextView.Properties.TryGetProperty( "KeyBindingTextAdorned", out bool adorned) && adorned; if (!alreadyAdorned) { new PurpleCornerBox((IWpfTextView)args.TextView); args.TextView.Properties.AddProperty("KeyBindingTextAdorned", true); } } return false; }- 将 KeyBindingTestTextViewCreationListener.cs 文件中的装饰层定义复制到 KeyBindingCommandHandler.cs ,然后删除 KeyBindingTestTextViewCreationListener.cs 文件:
 - /// <summary> /// Defines the adornment layer for the adornment. This layer is ordered /// after the selection layer in the Z-order. /// </summary> [Export(typeof(AdornmentLayerDefinition))] [Name("PurpleCornerBox")] [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)] private AdornmentLayerDefinition editorAdornmentLayer;
使装饰出现在每一行上
原始装饰出现在文本文件中的每个字符“a”上。 现在,我们已更改代码以添加装饰以响应 + 字符,因此它仅在键入字符的 + 行中添加装饰。 我们可以更改装饰代码,使装饰再次出现在每个“a”上。
在 KeyBindingTest.cs 文件中,更改 CreateVisuals() 方法以循环访问视图中的所有行以修饰“a”字符。
private void CreateVisuals(ITextViewLine line)
{
    IWpfTextViewLineCollection textViewLines = this.view.TextViewLines;
    foreach (ITextViewLine textViewLine in textViewLines)
    {
        if (textViewLine.ToString().Contains("a"))
        {
            // Loop through each character, and place a box around any 'a'
            for (int charIndex = textViewLine.Start; charIndex < textViewLine.End; charIndex++)
            {
                if (this.view.TextSnapshot[charIndex] == 'a')
                {
                    SnapshotSpan span = new SnapshotSpan(this.view.TextSnapshot, Span.FromBounds(charIndex, charIndex + 1));
                    Geometry geometry = textViewLines.GetMarkerGeometry(span);
                    if (geometry != null)
                    {
                        var drawing = new GeometryDrawing(this.brush, this.pen, geometry);
                        drawing.Freeze();
                        var drawingImage = new DrawingImage(drawing);
                        drawingImage.Freeze();
                        var image = new Image
                        {
                            Source = drawingImage,
                        };
                        // Align the image with the top of the bounds of the text geometry
                        Canvas.SetLeft(image, geometry.Bounds.Left);
                        Canvas.SetTop(image, geometry.Bounds.Top);
                        this.layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, image, null);
                    }
                }
            }
        }
    }
}
生成并测试代码
- 生成 KeyBindingTest 解决方案,并在实验实例中运行它。 
- 创建或打开文本文件。 键入一些包含字符“a”的单词,然后在文本视图中的任意位置键入 + 。 - 文件中的每个“a”字符上应显示一个紫色方块。