Compartilhar via


Demonstra Passo a passo: Usar uma tecla de atalho com uma extensão de editor

Você pode responder às teclas de atalho em sua extensão de editor. O passo a passo a seguir mostra como adicionar um adorno de exibição a um modo de exibição de texto usando uma tecla de atalho. Este passo a passo é baseado no modelo de editor de adorno do visor e permite que você adicione o adorno usando o caractere +.

Criar um projeto MEF (Managed Extensibility Framework)

  1. Crie um projeto C# VSIX. (No Caixa de diálogo Novo Projeto, selecione Visual C# / Extensibilidade e, em seguida, Projeto VSIX.) Nomeie a solução KeyBindingTest.

  2. Adicione um modelo de item de Adorno de Texto do Editor ao projeto e nomeie-o.KeyBindingTest Para obter mais informações, consulte Criar uma extensão com um modelo de item do editor.

  3. Adicione as seguintes referências e defina CopyLocal como false:

    Microsoft.VisualStudio.Editor

    Microsoft.VisualStudio.OLE.Interop

    Microsoft.VisualStudio.Shell.14.0

    Microsoft.VisualStudio.TextManager.Interop

    No arquivo de classe KeyBindingTest, altere o nome da classe para PurpleCornerBox. Use a lâmpada que aparece na margem esquerda para fazer as outras alterações apropriadas. Dentro do construtor, altere o nome da camada de adorno de KeyBindingTest para PurpleCornerBox:

this.layer = view.GetAdornmentLayer("PurpleCornerBox");

No arquivo de classe KeyBindingTestTextViewCreationListener.cs altere o nome do AdornmentLayer de KeyBindingTest para PurpleCornerBox:

[Export(typeof(AdornmentLayerDefinition))]
[Name("PurpleCornerBox")]
[Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
public AdornmentLayerDefinition editorAdornmentLayer;

Comando Manipular TYPECHAR

Antes do Visual Studio 2017 versão 15.6, a única maneira de manipular comandos em uma extensão de editor era implementando um IOleCommandTarget filtro de comando baseado. Visual Studio 2017 versão 15.6 introduziu uma abordagem simplificada moderna com base em manipuladores de comando do editor. As próximas duas seções demonstram como lidar com um comando usando a abordagem legada e moderna.

Definir o filtro de comando (anterior ao Visual Studio 2017 versão 15.6)

O filtro de comando é uma implementação do IOleCommandTarget, que manipula o comando instanciando o adorno.

  1. Adicione um arquivo de classe e nomeie-o KeyBindingCommandFilter.

  2. Adicione o seguinte usando diretivas.

    using System;
    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio.OLE.Interop;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Text.Editor;
    
    
  3. A classe chamada KeyBindingCommandFilter deve herdar de IOleCommandTarget.

    internal class KeyBindingCommandFilter : IOleCommandTarget
    
  4. Adicione campos particulares para o modo de exibição de texto, o próximo comando na cadeia de comandos e um sinalizador para representar se o filtro de comando já foi adicionado.

    private IWpfTextView m_textView;
    internal IOleCommandTarget m_nextTarget;
    internal bool m_added;
    internal bool m_adorned;
    
  5. Adicione um construtor que defina o modo de exibição de texto.

    public KeyBindingCommandFilter(IWpfTextView textView)
    {
        m_textView = textView;
        m_adorned = false;
    }
    
  6. Implemente o QueryStatus() método da seguinte maneira.

    int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
    {
        return m_nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
    }
    
  7. Implemente o método para que ele adicione uma caixa roxa ao modo de exibição se um caractere Exec() de sinal de adição (+) for digitado.

    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);
    }
    
    

Adicionar o filtro de comando (anterior ao Visual Studio 2017 versão 15.6)

O provedor de adorno deve adicionar um filtro de comando à exibição de texto. Neste exemplo, o provedor implementa para escutar eventos de criação de exibição de IVsTextViewCreationListener texto. Este provedor de adorno também exporta a camada de adorno, que define a ordem Z do adorno.

  1. No arquivo KeyBindingTestTextViewCreationListener, adicione o seguinte usando diretivas:

    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. Para obter o adaptador de exibição de texto, você deve importar o IVsEditorAdaptersFactoryService.

    [Import(typeof(IVsEditorAdaptersFactoryService))]
    internal IVsEditorAdaptersFactoryService editorFactory = null;
    
    
  3. Altere o método para que ele adicione o TextViewCreatedKeyBindingCommandFilter.

    public void TextViewCreated(IWpfTextView textView)
    {
        AddCommandFilter(textView, new KeyBindingCommandFilter(textView));
    }
    
  4. O AddCommandFilter manipulador obtém o adaptador de exibição de texto e adiciona o filtro de comando.

    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;
            }
        }
    }
    

Implementar um manipulador de comandos (a partir do Visual Studio 2017 versão 15.6)

Primeiro, atualize as referências Nuget do projeto para fazer referência à API do editor mais recente:

  1. Clique com o botão direito do mouse no projeto e selecione Gerenciar pacotes Nuget.

  2. No Gerenciador de Pacotes Nuget, selecione a guia Atualizações, marque a caixa de seleção Selecionar todos os pacotes e selecione Atualizar.

O manipulador de comandos é uma implementação do ICommandHandler<T>, que manipula o comando instanciando o adorno.

  1. Adicione um arquivo de classe e nomeie-o KeyBindingCommandHandler.

  2. Adicione o seguinte usando diretivas.

    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. A classe chamada KeyBindingCommandHandler deve herdar de ICommandHandler<TypeCharCommandArgs>e exportá-la como ICommandHandler:

    [Export(typeof(ICommandHandler))]
    [ContentType("text")]
    [Name("KeyBindingTest")]
    internal class KeyBindingCommandHandler : ICommandHandler<TypeCharCommandArgs>
    
  4. Adicione um nome de exibição do manipulador de comandos:

    public string DisplayName => "KeyBindingTest";
    
  5. Implemente o GetCommandState() método da seguinte maneira. Como esse manipulador de comandos manipula o comando TYPECHAR do editor principal, ele pode delegar a habilitação do comando ao editor principal.

    public CommandState GetCommandState(TypeCharCommandArgs args)
    {
        return CommandState.Unspecified;
    }
    
  6. Implemente o método para que ele adicione uma caixa roxa ao modo de exibição se um caractere ExecuteCommand() de sinal de adição (+) for digitado.

    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. Copie a definição da camada de adorno do arquivo KeyBindingTestTextViewCreationListener.cs para o arquivo KeyBindingCommandHandler.cs e exclua o arquivo 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;
    

Faça com que o adorno apareça em todas as linhas

O adorno original aparecia em cada caractere 'a' em um arquivo de texto. Agora que mudamos o código para adicionar o adorno em resposta ao + personagem, ele adiciona o adorno apenas na linha onde o + caractere é digitado. Podemos mudar o código do adorno para que o adorno apareça novamente em cada 'a'.

No arquivo KeyBindingTest.cs altere o método para iterar por todas as linhas no modo de exibição para decorar o caractere 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);
                    }
                }
            }
        }
    }
}

Compilar e testar o código

  1. Crie a solução KeyBindingTest e execute-a na instância experimental.

  2. Crie ou abra um arquivo de texto. Digite algumas palavras que contenham o caractere 'a' e, em seguida, digite + em qualquer lugar na exibição de texto.

    Um quadrado roxo deve aparecer em cada caractere 'a' no arquivo.