Поделиться через


Пошаговое руководство. Использование сочетания клавиш с расширением редактора

Вы можете ответить на сочетания клавиш в расширении редактора. В следующем пошаговом руководстве показано, как добавить украшение представления в текстовое представление с помощью сочетания клавиш. Это пошаговое руководство основано на шаблоне редактора украшений окна просмотра и позволяет добавлять украшения с помощью символа +.

Создание проекта Managed Extensibility Framework (MEF)

  1. Создайте проект VSIX на C#. (В Диалоговое окно "Новый проект" , выберите Visual C# / Расширяемость, а затем ПРОЕКТ VSIX.) Назовите решение KeyBindingTest.

  2. Добавьте шаблон элемента оформления текста редактора в проект и назовите его KeyBindingTest. Дополнительные сведения см. в разделе "Создание расширения с помощью шаблона элемента редактора".

  3. Добавьте следующие ссылки и задайте значение CopyLocalfalse:

    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, которая обрабатывает команду путем создания экземпляра украшения.

  1. Добавьте файл класса с именем KeyBindingCommandFilter.

  2. Добавьте следующие директивы using.

    using System;
    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio.OLE.Interop;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Text.Editor;
    
    
  3. Класс с именем KeyBindingCommandFilter должен наследоваться.IOleCommandTarget

    internal class KeyBindingCommandFilter : IOleCommandTarget
    
  4. Добавьте частные поля для текстового представления, следующую команду в цепочке команд и флаг, который будет представлять, уже добавлен ли фильтр команд.

    private IWpfTextView m_textView;
    internal IOleCommandTarget m_nextTarget;
    internal bool m_added;
    internal bool m_adorned;
    
  5. Добавьте конструктор, который задает текстовое представление.

    public KeyBindingCommandFilter(IWpfTextView textView)
    {
        m_textView = textView;
        m_adorned = false;
    }
    
  6. QueryStatus() Реализуйте метод следующим образом.

    int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
    {
        return m_nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
    }
    
  7. 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 украшения.

  1. В файле 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;
    
    
  2. Чтобы получить адаптер текстового представления, необходимо импортировать .IVsEditorAdaptersFactoryService

    [Import(typeof(IVsEditorAdaptersFactoryService))]
    internal IVsEditorAdaptersFactoryService editorFactory = null;
    
    
  3. Измените TextViewCreated метод таким образом, чтобы он добавил KeyBindingCommandFilter.

    public void TextViewCreated(IWpfTextView textView)
    {
        AddCommandFilter(textView, new KeyBindingCommandFilter(textView));
    }
    
  4. Обработчик 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 редактора:

  1. Щелкните проект правой кнопкой мыши и выберите пункт "Управление пакетами Nuget".

  2. В диспетчер пакетов Nuget перейдите на вкладку Обновления, выберите пункт "Выбрать все пакеты проверка", а затем нажмите кнопку "Обновить".

Обработчик команд — это реализация ICommandHandler<T>, которая обрабатывает команду путем создания экземпляра украшения.

  1. Добавьте файл класса с именем KeyBindingCommandHandler.

  2. Добавьте следующие директивы 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;
    
  3. Класс с именем KeyBindingCommandHandler должен наследоваться от ICommandHandler<TypeCharCommandArgs>и экспортировать его как ICommandHandler:

    [Export(typeof(ICommandHandler))]
    [ContentType("text")]
    [Name("KeyBindingTest")]
    internal class KeyBindingCommandHandler : ICommandHandler<TypeCharCommandArgs>
    
  4. Добавьте отображаемое имя обработчика команд:

    public string DisplayName => "KeyBindingTest";
    
  5. GetCommandState() Реализуйте метод следующим образом. Так как этот обработчик команд обрабатывает команду TYPECHAR для основного редактора, он может делегировать включение команды в основной редактор.

    public CommandState GetCommandState(TypeCharCommandArgs args)
    {
        return CommandState.Unspecified;
    }
    
  6. 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;
    }
    
    1. Скопируйте определение слоя украшений из файла KeyBindingTestTextCreationListener.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);
                    }
                }
            }
        }
    }
}

Сборка и проверка кода

  1. Создайте решение KeyBindingTest и запустите его в экспериментальном экземпляре.

  2. Создайте или откройте текстовый файл. Введите некоторые слова, содержащие символ "a", а затем введите + в любом месте текстового представления.

    Фиолетовый квадрат должен отображаться на каждом символе "a" в файле.