演练:将快捷键与编辑器扩展配合使用
可以在编辑器扩展中响应快捷键。 以下演练演示如何使用快捷键向文本视图添加视图装饰。 本演练基于视区装饰编辑器模板,它允许你使用 + 字符添加装饰。
创建托管扩展性框架 (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”字符上应显示一个紫色方块。