다음을 통해 공유


연습: 편집기 확장과 함께 바로 가기 키 사용

편집기 확장에서 바로 가기 키에 응답할 수 있습니다. 다음 연습에서는 바로 가기 키를 사용하여 보기 영역을 텍스트 보기에 추가하는 방법을 보여 줍니다. 이 연습은 뷰포트 영역 편집기 템플릿을 기반으로 하며 + 문자를 사용하여 영역을 추가할 수 있습니다.

MEF(Managed Extensibility Framework) 프로젝트 만들기

  1. C# VSIX 프로젝트를 만듭니다. (새 프로젝트 대화 상자에서 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. 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);
                    }
                }
            }
        }
    }
}

코드 빌드 및 테스트

  1. KeyBindingTest 솔루션을 빌드하고 실험적 인스턴스에서 실행합니다.

  2. 텍스트 파일을 만들거나 엽니다. 문자 'a'가 포함된 단어를 입력한 다음 텍스트 보기의 아무 곳에나 +를 입력합니다.

    파일의 모든 'a' 문자에 자주색 사각형이 표시됩니다.