Návod: Použití příkazu prostředí s rozšířením editoru
Z balíčku VSPackage můžete do editoru přidat funkce, jako jsou příkazy nabídky. Tento názorný postup ukazuje, jak přidat doplněk do textového zobrazení v editoru vyvoláním příkazu nabídky.
Tento názorný postup ukazuje použití balíčku VSPackage společně s komponentou MEF (Managed Extensibility Framework). K registraci příkazu nabídky v prostředí sady Visual Studio musíte použít balíček VSPackage. A pomocí příkazu můžete získat přístup k části součásti MEF.
Vytvoření rozšíření pomocí příkazu nabídky
Vytvořte balíček VSPackage, který vloží příkaz nabídky s názvem Přidat adornment v nabídce Nástroje .
Vytvořte projekt VSIX jazyka C# s názvem
MenuCommandTest
a přidejte šablonu vlastní položky příkazu AddAdornment. Další informace najdete v tématu Vytvoření rozšíření pomocí příkazu nabídky.Otevře se řešení s názvem MenuCommandTest. Soubor MenuCommandTestPackage obsahuje kód, který vytvoří příkaz nabídky a umístí ho do nabídky Nástroje . V tomto okamžiku příkaz jenom způsobí, že se zobrazí okno se zprávou. V pozdějších krocích se dozvíte, jak to změnit, aby se zobrazilo doplňkové doplňky komentáře.
Otevřete soubor source.extension.vsixmanifest v editoru manifestu VSIX. Karta
Assets
by měla obsahovat řádek pro Microsoft.VisualStudio.VsPackage s názvem MenuCommandTest.Uložte a zavřete soubor source.extension.vsixmanifest .
Přidání rozšíření MEF do rozšíření příkazů
V Průzkumník řešení klepněte pravým tlačítkem myši na uzel řešení, klepněte na tlačítko Přidat a potom klepněte na příkaz Nový projekt. V dialogovém okně Přidat nový projekt klikněte v části Visual C# na rozšiřitelnost a potom na projekt VSIX. Pojmenujte projekt
CommentAdornmentTest
.Vzhledem k tomu, že tento projekt bude pracovat se sestavením VSPackage se silným názvem, musíte sestavení podepsat. Soubor klíče, který už je vytvořený pro sestavení VSPackage, můžete znovu použít.
Otevřete vlastnosti projektu a vyberte kartu Podepisování .
Vyberte Podepsat sestavení.
V části Zvolit soubor silného názvu klíče vyberte soubor Key.snk , který byl vygenerován pro sestavení MenuCommandTest.
Odkaz na rozšíření MEF v projektu VSPackage
Vzhledem k tomu, že do balíčku VSPackage přidáváte komponentu MEF, musíte v manifestu zadat oba druhy prostředků.
Poznámka:
Další informace o MEF naleznete v tématu Rozhraní MEF (Managed Extensibility Framework) (MEF).
Odkazování na komponentu MEF v projektu VSPackage
V projektu MenuCommandTest otevřete soubor source.extension.vsixmanifest v editoru manifestu VSIX.
Na kartě Prostředky klikněte na Tlačítko Nový.
V seznamu Typ zvolte Microsoft.VisualStudio.MefComponent.
V seznamu Zdroj zvolte projekt v aktuálním řešení.
V seznamu projektu zvolte CommentAdornmentTest.
Uložte a zavřete soubor source.extension.vsixmanifest .
Ujistěte se, že projekt MenuCommandTest má odkaz na projekt CommentAdornmentTest.
V projektu CommentAdornmentTest nastavte projekt tak, aby vytvořil sestavení. V Průzkumník řešení vyberte projekt a vyhledejte v okně Vlastnosti pro vlastnost Kopírovat výstup sestavení do OutputDirectory a nastavte ho na true.
Definování doplňku komentáře
Samotný doplněk komentáře se skládá z ITrackingSpan textu, který sleduje vybraný text, a některé řetězce, které představují autora a popis textu.
Definování doplňku komentáře
V projektu CommentAdornmentTest přidejte nový soubor třídy a pojmenujte jej
CommentAdornment
.Přidejte následující odkazy:
Microsoft.VisualStudio.CoreUtility
Microsoft.VisualStudio.Text.Data
Microsoft.VisualStudio.Text.Logic
Microsoft.VisualStudio.Text.UI
Microsoft.VisualStudio.Text.UI.Wpf
System.ComponentModel.Composition
Presentationcore
Presentationframework
Windowsbase
Přidejte následující
using
direktivu.using Microsoft.VisualStudio.Text;
Soubor by měl obsahovat třídu s názvem
CommentAdornment
.internal class CommentAdornment
Přidejte do
CommentAdornment
třídy tři pole pro ITrackingSpanautora a popis.public readonly ITrackingSpan Span; public readonly string Author; public readonly string Text;
Přidejte konstruktor, který inicializuje pole.
public CommentAdornment(SnapshotSpan span, string author, string text) { this.Span = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeExclusive); this.Author = author; this.Text = text; }
Vytvoření vizuálního prvku pro doplněk
Definujte vizuální prvek pro vaše doplňky. Pro účely tohoto návodu definujte ovládací prvek, který dědí z třídy CanvasWindows Presentation Foundation (WPF).
Vytvořte třídu v projektu CommentAdornmentTest a pojmenujte ji
CommentBlock
.Přidejte následující
using
direktivy.using Microsoft.VisualStudio.Text; using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities;
CommentBlock
Nastavení třídy zdědí z Canvas.internal class CommentBlock : Canvas { }
Přidejte některá soukromá pole pro definování vizuálních aspektů doplňku.
private Geometry textGeometry; private Grid commentGrid; private static Brush brush; private static Pen solidPen; private static Pen dashPen;
Přidejte konstruktor, který definuje doplněk komentáře a přidá příslušný text.
public CommentBlock(double textRightEdge, double viewRightEdge, Geometry newTextGeometry, string author, string body) { if (brush == null) { brush = new SolidColorBrush(Color.FromArgb(0x20, 0x00, 0xff, 0x00)); brush.Freeze(); Brush penBrush = new SolidColorBrush(Colors.Green); penBrush.Freeze(); solidPen = new Pen(penBrush, 0.5); solidPen.Freeze(); dashPen = new Pen(penBrush, 0.5); dashPen.DashStyle = DashStyles.Dash; dashPen.Freeze(); } this.textGeometry = newTextGeometry; TextBlock tb1 = new TextBlock(); tb1.Text = author; TextBlock tb2 = new TextBlock(); tb2.Text = body; const int MarginWidth = 8; this.commentGrid = new Grid(); this.commentGrid.RowDefinitions.Add(new RowDefinition()); this.commentGrid.RowDefinitions.Add(new RowDefinition()); ColumnDefinition cEdge = new ColumnDefinition(); cEdge.Width = new GridLength(MarginWidth); ColumnDefinition cEdge2 = new ColumnDefinition(); cEdge2.Width = new GridLength(MarginWidth); this.commentGrid.ColumnDefinitions.Add(cEdge); this.commentGrid.ColumnDefinitions.Add(new ColumnDefinition()); this.commentGrid.ColumnDefinitions.Add(cEdge2); System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle(); rect.RadiusX = 6; rect.RadiusY = 3; rect.Fill = brush; rect.Stroke = Brushes.Green; Size inf = new Size(double.PositiveInfinity, double.PositiveInfinity); tb1.Measure(inf); tb2.Measure(inf); double middleWidth = Math.Max(tb1.DesiredSize.Width, tb2.DesiredSize.Width); this.commentGrid.Width = middleWidth + 2 * MarginWidth; Grid.SetColumn(rect, 0); Grid.SetRow(rect, 0); Grid.SetRowSpan(rect, 2); Grid.SetColumnSpan(rect, 3); Grid.SetRow(tb1, 0); Grid.SetColumn(tb1, 1); Grid.SetRow(tb2, 1); Grid.SetColumn(tb2, 1); this.commentGrid.Children.Add(rect); this.commentGrid.Children.Add(tb1); this.commentGrid.Children.Add(tb2); Canvas.SetLeft(this.commentGrid, Math.Max(viewRightEdge - this.commentGrid.Width - 20.0, textRightEdge + 20.0)); Canvas.SetTop(this.commentGrid, textGeometry.GetRenderBounds(solidPen).Top); this.Children.Add(this.commentGrid); }
Implementujte také obslužnou rutinu OnRender události, která nakreslí doplňkovou ozdobu.
protected override void OnRender(DrawingContext dc) { base.OnRender(dc); if (this.textGeometry != null) { dc.DrawGeometry(brush, solidPen, this.textGeometry); Rect textBounds = this.textGeometry.GetRenderBounds(solidPen); Point p1 = new Point(textBounds.Right, textBounds.Bottom); Point p2 = new Point(Math.Max(Canvas.GetLeft(this.commentGrid) - 20.0, p1.X), p1.Y); Point p3 = new Point(Math.Max(Canvas.GetLeft(this.commentGrid), p1.X), (Canvas.GetTop(this.commentGrid) + p1.Y) * 0.5); dc.DrawLine(dashPen, p1, p2); dc.DrawLine(dashPen, p2, p3); } }
Přidání IWpfTextViewCreationListener
Je IWpfTextViewCreationListener součástí komponenty MEF, kterou můžete použít k naslouchání zobrazení událostí vytváření.
Přidejte soubor třídy do projektu CommentAdornmentTest a pojmenujte ho
Connector
.Přidejte následující
using
direktivy.using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities;
Deklarujte třídu, která implementuje IWpfTextViewCreationListenera exportuje s ContentTypeAttribute "text" a a TextViewRoleAttribute z Document. Atribut typu obsahu určuje druh obsahu, na který se komponenta vztahuje. Textový typ je základním typem pro všechny nebinární typy souborů. Proto bude téměř každé vytvořené textové zobrazení tohoto typu. Atribut role textového zobrazení určuje druh textového zobrazení, na které se komponenta vztahuje. Role zobrazení textu dokumentu obvykle zobrazují text, který se skládá z řádků a je uložený v souboru.
Implementujte metodu TextViewCreated tak, aby volala statickou
Create()
událostCommentAdornmentManager
.public void TextViewCreated(IWpfTextView textView) { CommentAdornmentManager.Create(textView); }
Přidejte metodu, kterou můžete použít ke spuštění příkazu.
static public void Execute(IWpfTextViewHost host) { IWpfTextView view = host.TextView; //Add a comment on the selected text. if (!view.Selection.IsEmpty) { //Get the provider for the comment adornments in the property bag of the view. CommentAdornmentProvider provider = view.Properties.GetProperty<CommentAdornmentProvider>(typeof(CommentAdornmentProvider)); //Add some arbitrary author and comment text. string author = System.Security.Principal.WindowsIdentity.GetCurrent().Name; string comment = "Four score...."; //Add the comment adornment using the provider. provider.Add(view.Selection.SelectedSpans[0], author, comment); } }
Definování vrstvy doplňku
Pokud chcete přidat nové doplňkové doplňky, musíte definovat vrstvu doplňku.
Definování vrstvy doplňku
Connector
Ve třídě deklarujte veřejné pole typu AdornmentLayerDefinitiona vyexportujte ho s NameAttribute jedinečným názvem vrstvy doplňku a definuje OrderAttribute vztah pořadí vykreslování této vrstvy doplňku k ostatním vrstvám zobrazení textu (text, stříšku a výběr).[Export(typeof(AdornmentLayerDefinition))] [Name("CommentAdornmentLayer")] [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)] public AdornmentLayerDefinition commentLayerDefinition;
Zadejte doplňky komentářů.
Při definování doplňku také implementujte zprostředkovatele doplňku komentáře a správce doplňku komentáře. Poskytovatel doplňku komentáře uchovává seznam doplňků komentářů, naslouchá Changed událostem v podkladové vyrovnávací paměti textu a odstraňuje doplňky komentářů při odstranění podkladového textu.
Přidejte nový soubor třídy do projektu CommentAdornmentTest a pojmenujte ho
CommentAdornmentProvider
.Přidejte následující
using
direktivy.using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor;
Přidejte třídu s názvem
CommentAdornmentProvider
.internal class CommentAdornmentProvider { }
Přidejte soukromá pole pro vyrovnávací paměť textu a seznam doplňků komentářů souvisejících s vyrovnávací pamětí.
private ITextBuffer buffer; private IList<CommentAdornment> comments = new List<CommentAdornment>();
Přidání konstruktoru pro
CommentAdornmentProvider
. Tento konstruktor by měl mít privátní přístup, protože zprostředkovatel je vytvořena metodouCreate()
. Konstruktor přidá obslužnou rutinuOnBufferChanged
Changed události do události.private CommentAdornmentProvider(ITextBuffer buffer) { this.buffer = buffer; //listen to the Changed event so we can react to deletions. this.buffer.Changed += OnBufferChanged; }
Přidejte metodu
Create()
.public static CommentAdornmentProvider Create(IWpfTextView view) { return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentProvider>(delegate { return new CommentAdornmentProvider(view.TextBuffer); }); }
Přidejte metodu
Detach()
.public void Detach() { if (this.buffer != null) { //remove the Changed listener this.buffer.Changed -= OnBufferChanged; this.buffer = null; } }
Přidejte obslužnou rutinu
OnBufferChanged
události.private void OnBufferChanged(object sender, TextContentChangedEventArgs e) { //Make a list of all comments that have a span of at least one character after applying the change. There is no need to raise a changed event for the deleted adornments. The adornments are deleted only if a text change would cause the view to reformat the line and discard the adornments. IList<CommentAdornment> keptComments = new List<CommentAdornment>(this.comments.Count); foreach (CommentAdornment comment in this.comments) { Span span = comment.Span.GetSpan(e.After); //if a comment does not span at least one character, its text was deleted. if (span.Length != 0) { keptComments.Add(comment); } } this.comments = keptComments; }
Přidejte deklaraci události
CommentsChanged
.public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
Vytvořte metodu
Add()
pro přidání doplňku.public void Add(SnapshotSpan span, string author, string text) { if (span.Length == 0) throw new ArgumentOutOfRangeException("span"); if (author == null) throw new ArgumentNullException("author"); if (text == null) throw new ArgumentNullException("text"); //Create a comment adornment given the span, author and text. CommentAdornment comment = new CommentAdornment(span, author, text); //Add it to the list of comments. this.comments.Add(comment); //Raise the changed event. EventHandler<CommentsChangedEventArgs> commentsChanged = this.CommentsChanged; if (commentsChanged != null) commentsChanged(this, new CommentsChangedEventArgs(comment, null)); }
Přidejte metodu
RemoveComments()
.public void RemoveComments(SnapshotSpan span) { EventHandler<CommentsChangedEventArgs> commentsChanged = this.CommentsChanged; //Get a list of all the comments that are being kept IList<CommentAdornment> keptComments = new List<CommentAdornment>(this.comments.Count); foreach (CommentAdornment comment in this.comments) { //find out if the given span overlaps with the comment text span. If two spans are adjacent, they do not overlap. To consider adjacent spans, use IntersectsWith. if (comment.Span.GetSpan(span.Snapshot).OverlapsWith(span)) { //Raise the change event to delete this comment. if (commentsChanged != null) commentsChanged(this, new CommentsChangedEventArgs(null, comment)); } else keptComments.Add(comment); } this.comments = keptComments; }
Přidejte metodu
GetComments()
, která vrátí všechny komentáře v daném rozsahu snímků.public Collection<CommentAdornment> GetComments(SnapshotSpan span) { IList<CommentAdornment> overlappingComments = new List<CommentAdornment>(); foreach (CommentAdornment comment in this.comments) { if (comment.Span.GetSpan(span.Snapshot).OverlapsWith(span)) overlappingComments.Add(comment); } return new Collection<CommentAdornment>(overlappingComments); }
Přidejte třídu s názvem
CommentsChangedEventArgs
, následujícím způsobem.internal class CommentsChangedEventArgs : EventArgs { public readonly CommentAdornment CommentAdded; public readonly CommentAdornment CommentRemoved; public CommentsChangedEventArgs(CommentAdornment added, CommentAdornment removed) { this.CommentAdded = added; this.CommentRemoved = removed; } }
Správa doplňků komentářů
Správce doplňků komentáře vytvoří doplněk a přidá ho do vrstvy doplňku. Naslouchá událostem LayoutChanged a Closed událostem, aby bylo možné doplňky přesunout nebo odstranit. Také naslouchá CommentsChanged
události, která se aktivuje poskytovatelem doplňku komentáře při přidání nebo odebrání komentářů.
Přidejte soubor třídy do projektu CommentAdornmentTest a pojmenujte ho
CommentAdornmentManager
.Přidejte následující
using
direktivy.using System; using System.Collections.Generic; using System.Windows.Media; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting;
Přidejte třídu s názvem
CommentAdornmentManager
.internal class CommentAdornmentManager { }
Přidejte některá soukromá pole.
private readonly IWpfTextView view; private readonly IAdornmentLayer layer; private readonly CommentAdornmentProvider provider;
Přidejte konstruktor, který přihlásí správce k odběru LayoutChanged událostí a Closed událostí
CommentsChanged
. Konstruktor je privátní, protože správce vytvoří instanci statickouCreate()
metodou.private CommentAdornmentManager(IWpfTextView view) { this.view = view; this.view.LayoutChanged += OnLayoutChanged; this.view.Closed += OnClosed; this.layer = view.GetAdornmentLayer("CommentAdornmentLayer"); this.provider = CommentAdornmentProvider.Create(view); this.provider.CommentsChanged += OnCommentsChanged; }
V případě potřeby přidejte metodu
Create()
, která získá zprostředkovatele nebo ji vytvoří.public static CommentAdornmentManager Create(IWpfTextView view) { return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentManager>(delegate { return new CommentAdornmentManager(view); }); }
Přidejte obslužnou rutinu
CommentsChanged
.private void OnCommentsChanged(object sender, CommentsChangedEventArgs e) { //Remove the comment (when the adornment was added, the comment adornment was used as the tag). if (e.CommentRemoved != null) this.layer.RemoveAdornmentsByTag(e.CommentRemoved); //Draw the newly added comment (this will appear immediately: the view does not need to do a layout). if (e.CommentAdded != null) this.DrawComment(e.CommentAdded); }
Přidejte obslužnou rutinu Closed .
private void OnClosed(object sender, EventArgs e) { this.provider.Detach(); this.view.LayoutChanged -= OnLayoutChanged; this.view.Closed -= OnClosed; }
Přidejte obslužnou rutinu LayoutChanged .
private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { //Get all of the comments that intersect any of the new or reformatted lines of text. List<CommentAdornment> newComments = new List<CommentAdornment>(); //The event args contain a list of modified lines and a NormalizedSpanCollection of the spans of the modified lines. //Use the latter to find the comments that intersect the new or reformatted lines of text. foreach (Span span in e.NewOrReformattedSpans) { newComments.AddRange(this.provider.GetComments(new SnapshotSpan(this.view.TextSnapshot, span))); } //It is possible to get duplicates in this list if a comment spanned 3 lines, and the first and last lines were modified but the middle line was not. //Sort the list and skip duplicates. newComments.Sort(delegate(CommentAdornment a, CommentAdornment b) { return a.GetHashCode().CompareTo(b.GetHashCode()); }); CommentAdornment lastComment = null; foreach (CommentAdornment comment in newComments) { if (comment != lastComment) { lastComment = comment; this.DrawComment(comment); } } }
Přidejte soukromou metodu, která nakreslí komentář.
private void DrawComment(CommentAdornment comment) { SnapshotSpan span = comment.Span.GetSpan(this.view.TextSnapshot); Geometry g = this.view.TextViewLines.GetMarkerGeometry(span); if (g != null) { //Find the rightmost coordinate of all the lines that intersect the adornment. double maxRight = 0.0; foreach (ITextViewLine line in this.view.TextViewLines.GetTextViewLinesIntersectingSpan(span)) maxRight = Math.Max(maxRight, line.Right); //Create the visualization. CommentBlock block = new CommentBlock(maxRight, this.view.ViewportRight, g, comment.Author, comment.Text); //Add it to the layer. this.layer.AddAdornment(span, comment, block); } }
Přidání doplňku komentáře pomocí příkazu nabídky
Příkaz nabídky můžete použít k vytvoření doplňku komentáře implementací MenuItemCallback
metody VSPackage.
Do projektu MenuCommandTest přidejte následující odkazy:
Microsoft.VisualStudio.TextManager.Interop
Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Text.UI.Wpf
Otevřete soubor AddAdornment.cs a přidejte následující
using
direktivy.using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Editor; using CommentAdornmentTest;
Odstraňte metodu
Execute()
a přidejte následující obslužnou rutinu příkazu.private async void AddAdornmentHandler(object sender, EventArgs e) { }
Přidejte kód pro získání aktivního zobrazení. Abyste získali aktivní
IVsTextView
, musíte získatSVsTextManager
prostředí sady Visual Studio .private async void AddAdornmentHandler(object sender, EventArgs e) { IVsTextManager txtMgr = (IVsTextManager) await ServiceProvider.GetServiceAsync(typeof(SVsTextManager)); IVsTextView vTextView = null; int mustHaveFocus = 1; txtMgr.GetActiveView(mustHaveFocus, null, out vTextView); }
Pokud je toto textové zobrazení instancí textového zobrazení editoru, můžete ho přetypovat do IVsUserData rozhraní a získat IWpfTextViewHost a jeho přidružené IWpfTextView. IWpfTextViewHost Použijte k volání
Connector.Execute()
metody, která získá zprostředkovatele doplňku komentáře a přidá ozdobné doplňky. Obslužná rutina příkazu by teď měla vypadat takto:private async void AddAdornmentHandler(object sender, EventArgs e) { IVsTextManager txtMgr = (IVsTextManager) await ServiceProvider.GetServiceAsync(typeof(SVsTextManager)); IVsTextView vTextView = null; int mustHaveFocus = 1; txtMgr.GetActiveView(mustHaveFocus, null, out vTextView); IVsUserData userData = vTextView as IVsUserData; if (userData == null) { Console.WriteLine("No text view is currently open"); return; } IWpfTextViewHost viewHost; object holder; Guid guidViewHost = DefGuidList.guidIWpfTextViewHost; userData.GetData(ref guidViewHost, out holder); viewHost = (IWpfTextViewHost)holder; Connector.Execute(viewHost); }
Nastavte AddAdornmentHandler metoda jako obslužnou rutinu pro AddAdornment příkaz v AddAdornment konstruktoru.
private AddAdornment(AsyncPackage package, OleMenuCommandService commandService) { this.package = package ?? throw new ArgumentNullException(nameof(package)); commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); var menuCommandID = new CommandID(CommandSet, CommandId); var menuItem = new MenuCommand(this.AddAdornmentHandler, menuCommandID); commandService.AddCommand(menuItem); }
Sestavení a otestování kódu
Sestavte řešení a spusťte ladění. Měla by se zobrazit experimentální instance.
Vytvořte textový soubor. Zadejte nějaký text a vyberte ho.
V nabídce Nástroje klepněte na tlačítko Vyvolat přidat adornment. Bublina by se měla zobrazit na pravé straně okna textu a měla by obsahovat text podobný následujícímu textu.
YourUserName
Fourscore...