チュートリアル: エディター拡張機能でショートカット キーを使用する
エディター拡張機能でショートカット キーに応答することができます。 次のチュートリアルでは、ショートカット キーを使用してテキスト ビューにビュー修飾を追加する方法について説明します。 このチュートリアルは、ビューポート修飾エディター テンプレートに基づいており、+ 文字を使用して修飾を追加できます。
Managed Extensibility Framework (MEF) プロジェクトを作成する
C# VSIX プロジェクトを作成します。 ([新しいプロジェクト] ダイアログで、[Visual C#]、[拡張機能]、[VSIX プロジェクト] の順に選択します。) ソリューションに
KeyBindingTest
という名前を付けます。Editor Text Adornment 項目テンプレートをプロジェクトに追加し、
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 では、エディター コマンド ハンドラーに基づく最新の簡略化されたアプローチが導入されました。 次の 2 つのセクションでは、従来のアプローチと最新のアプローチの両方を使用してコマンドを処理する方法について説明します。
コマンド フィルターを定義する (Visual Studio 2017 バージョン 15.6 より前)
コマンド フィルターは、修飾をインスタンス化することによってコマンドを処理する IOleCommandTarget の実装です。
クラス ファイルを追加し、その名前を
KeyBindingCommandFilter
にします。ディレクティブを使用して以下を追加します。
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;
KeyBindingCommandFilter
を追加するように TextViewCreated メソッドを変更します。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 以降)
まず、最新のエディター API を参照するようにプロジェクトの Nuget 参照を更新します。
プロジェクトを右クリックし、[NuGet パッケージの管理] を選択します。
Nuget パッケージ マネージャーで、[更新] タブを選択し、[すべてのパッケージを選択] チェックボックスをオンにしてから、[更新] を選択します。
コマンド ハンドラーは、修飾をインスタンス化することによってコマンドを処理する ICommandHandler<T> の実装です。
クラス ファイルを追加し、その名前を
KeyBindingCommandHandler
にします。ディレクティブを使用して以下を追加します。
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 ファイルで、ビュー内のすべての行を反復処理し、"a" 文字を修飾するように CreateVisuals()
メソッドを変更します。
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" 文字に紫色の四角形が表示されます。