Panduan: Menggunakan perintah shell dengan ekstensi editor
Dari VSPackage, Anda dapat menambahkan fitur seperti perintah menu ke editor. Panduan ini menunjukkan cara menambahkan hiasan ke tampilan teks di editor dengan memanggil perintah menu.
Panduan ini menunjukkan penggunaan VSPackage bersama dengan bagian komponen Managed Extensibility Framework (MEF). Anda harus menggunakan VSPackage untuk mendaftarkan perintah menu dengan shell Visual Studio. Dan, Anda dapat menggunakan perintah untuk mengakses bagian komponen MEF.
Membuat ekstensi dengan perintah menu
Buat VSPackage yang menempatkan perintah menu bernama Tambahkan Hiasan pada menu Alat .
Buat proyek C# VSIX bernama
MenuCommandTest
, dan tambahkan nama templat item Perintah Kustom AddAdornment. Untuk informasi selengkapnya, lihat Membuat ekstensi dengan perintah menu.Solusi bernama MenuCommandTest terbuka. File MenuCommandTestPackage memiliki kode yang membuat perintah menu dan meletakkannya di menu Alat . Pada titik ini, perintah hanya menyebabkan kotak pesan muncul. Langkah-langkah selanjutnya akan menunjukkan cara mengubah ini untuk menampilkan hiasan komentar.
Buka file source.extension.vsixmanifest di Editor Manifes VSIX. Tab
Assets
harus memiliki baris untuk Microsoft.VisualStudio.VsPackage bernama MenuCommandTest.Simpan dan tutup file source.extension.vsixmanifest .
Menambahkan ekstensi MEF ke ekstensi perintah
Di Penjelajah Solusi, klik kanan simpul solusi, klik Tambahkan, lalu klik Proyek Baru. Dalam kotak dialog Tambahkan Proyek Baru, klik Ekstensibilitas di bawah Visual C#, lalu Proyek VSIX. Beri nama proyek
CommentAdornmentTest
.Karena proyek ini akan berinteraksi dengan rakitan VSPackage bernama kuat, Anda harus menandatangani rakitan. Anda dapat menggunakan kembali file kunci yang sudah dibuat untuk rakitan VSPackage.
Buka properti proyek dan pilih tab Penandatanganan .
Pilih Tanda tangani rakitan.
Di bawah Pilih file kunci nama yang kuat, pilih file Key.snk yang dihasilkan untuk rakitan MenuCommandTest.
Lihat ekstensi MEF dalam proyek VSPackage
Karena Anda menambahkan komponen MEF ke VSPackage, Anda harus menentukan kedua jenis aset dalam manifes.
Catatan
Untuk mendapatkan informasi selengkapnya tentang MEF, lihat Managed Extensibility Framework (MEF).
Untuk merujuk ke komponen MEF dalam proyek VSPackage
Dalam proyek MenuCommandTest, buka file source.extension.vsixmanifest di Editor Manifes VSIX.
Pada tab Aset , klik Baru.
Di daftar Jenis, pilih Microsoft.VisualStudio.MefComponent.
Di daftar Sumber, pilih Proyek dalam solusi saat ini.
Di daftar Proyek, pilih KomentarAdornmentTest.
Simpan dan tutup file source.extension.vsixmanifest .
Pastikan bahwa proyek MenuCommandTest memiliki referensi ke proyek CommentAdornmentTest.
Dalam proyek CommentAdornmentTest, atur proyek untuk menghasilkan assembly. Di Penjelajah Solusi, pilih proyek dan lihat di jendela Properti untuk properti Salin Output Build ke OutputDirectory, dan atur ke true.
Menentukan hiasan komentar
Hiasan komentar itu sendiri terdiri dari ITrackingSpan yang melacak teks yang dipilih, dan beberapa string yang mewakili penulis dan deskripsi teks.
Untuk menentukan hiasan komentar
Dalam proyek CommentAdornmentTest, tambahkan file kelas baru dan beri nama
CommentAdornment
.Tambahkan referensi berikut:
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
Tambahkan direktif berikut
using
.using Microsoft.VisualStudio.Text;
File harus berisi kelas bernama
CommentAdornment
.internal class CommentAdornment
Tambahkan tiga bidang ke
CommentAdornment
kelas untuk ITrackingSpan, penulis, dan deskripsi.public readonly ITrackingSpan Span; public readonly string Author; public readonly string Text;
Tambahkan konstruktor yang menginisialisasi bidang.
public CommentAdornment(SnapshotSpan span, string author, string text) { this.Span = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeExclusive); this.Author = author; this.Text = text; }
Membuat elemen visual untuk hiasan
Tentukan elemen visual untuk hiasan Anda. Untuk panduan ini, tentukan kontrol yang mewarisi dari kelas CanvasWindows Presentation Foundation (WPF).
Buat kelas di proyek CommentAdornmentTest, dan beri nama
CommentBlock
.Tambahkan direktif berikut
using
.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;
Buat kelas mewarisi
CommentBlock
dari Canvas.internal class CommentBlock : Canvas { }
Tambahkan beberapa bidang privat untuk menentukan aspek visual hiasan.
private Geometry textGeometry; private Grid commentGrid; private static Brush brush; private static Pen solidPen; private static Pen dashPen;
Tambahkan konstruktor yang menentukan hiasan komentar dan menambahkan teks yang relevan.
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); }
Terapkan juga penanganan OnRender aktivitas yang menarik hiasan.
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); } }
Menambahkan IWpfTextViewCreationListener
IWpfTextViewCreationListener adalah bagian komponen MEF yang dapat Anda gunakan untuk mendengarkan melihat peristiwa pembuatan.
Tambahkan file kelas ke proyek CommentAdornmentTest dan beri nama
Connector
.Tambahkan direktif berikut
using
.using System.ComponentModel.Composition; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities;
Deklarasikan kelas yang mengimplementasikan IWpfTextViewCreationListener, dan mengekspornya dengan ContentTypeAttribute "teks" dan TextViewRoleAttribute dari Document. Atribut jenis konten menentukan jenis konten yang diterapkan komponen. Jenis teks adalah jenis dasar untuk semua jenis file non-biner. Oleh karena itu, hampir setiap tampilan teks yang dibuat akan berjenis ini. Atribut peran tampilan teks menentukan jenis tampilan teks yang diterapkan komponen. Peran tampilan teks dokumen umumnya memperlihatkan teks yang terdiri dari baris dan disimpan dalam file.
Terapkan TextViewCreated metode sehingga memanggil peristiwa statis
Create()
dariCommentAdornmentManager
.public void TextViewCreated(IWpfTextView textView) { CommentAdornmentManager.Create(textView); }
Tambahkan metode yang dapat Anda gunakan untuk menjalankan perintah.
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); } }
Menentukan lapisan hiasan
Untuk menambahkan hiasan baru, Anda harus menentukan lapisan hiasan.
Untuk menentukan lapisan hiasan
Connector
Di kelas , deklarasikan bidang publik jenis AdornmentLayerDefinition, dan ekspor dengan NameAttribute yang menentukan nama unik untuk lapisan hiasan dan OrderAttribute yang menentukan hubungan urutan Z lapisan hiasan ini ke lapisan tampilan teks lainnya (teks, tanda sisipan, dan pilihan).[Export(typeof(AdornmentLayerDefinition))] [Name("CommentAdornmentLayer")] [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)] public AdornmentLayerDefinition commentLayerDefinition;
Berikan hiasan komentar
Saat Anda menentukan hiasan, terapkan juga penyedia hiasan komentar dan manajer hiasan komentar. Penyedia hiasan komentar menyimpan daftar hiasan komentar, mendengarkan Changed peristiwa pada buffer teks yang mendasarinya, dan menghapus hiasan komentar saat teks yang mendasarinya dihapus.
Tambahkan file kelas baru ke proyek CommentAdornmentTest dan beri nama
CommentAdornmentProvider
.Tambahkan direktif berikut
using
.using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor;
Tambahkan kelas bernama
CommentAdornmentProvider
.internal class CommentAdornmentProvider { }
Tambahkan bidang privat untuk buffer teks dan daftar hiasan komentar yang terkait dengan buffer.
private ITextBuffer buffer; private IList<CommentAdornment> comments = new List<CommentAdornment>();
Tambahkan konstruktor untuk
CommentAdornmentProvider
. Konstruktor ini harus memiliki akses privat karena penyedia dibuat olehCreate()
metode . Konstruktor menambahkanOnBufferChanged
penanganan aktivitas ke Changed peristiwa.private CommentAdornmentProvider(ITextBuffer buffer) { this.buffer = buffer; //listen to the Changed event so we can react to deletions. this.buffer.Changed += OnBufferChanged; }
Tambahkan metode
Create()
.public static CommentAdornmentProvider Create(IWpfTextView view) { return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentProvider>(delegate { return new CommentAdornmentProvider(view.TextBuffer); }); }
Tambahkan metode
Detach()
.public void Detach() { if (this.buffer != null) { //remove the Changed listener this.buffer.Changed -= OnBufferChanged; this.buffer = null; } }
Tambahkan penanganan
OnBufferChanged
aktivitas.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; }
Tambahkan deklarasi untuk suatu
CommentsChanged
acara.public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
Buat
Add()
metode untuk menambahkan hiasan.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)); }
RemoveComments()
Tambahkan metode .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; }
GetComments()
Tambahkan metode yang mengembalikan semua komentar dalam rentang rekam jepret tertentu.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); }
Tambahkan kelas bernama
CommentsChangedEventArgs
, sebagai berikut.internal class CommentsChangedEventArgs : EventArgs { public readonly CommentAdornment CommentAdded; public readonly CommentAdornment CommentRemoved; public CommentsChangedEventArgs(CommentAdornment added, CommentAdornment removed) { this.CommentAdded = added; this.CommentRemoved = removed; } }
Mengelola hiasan komentar
Manajer hiasan komentar membuat hiasan dan menambahkannya ke lapisan hiasan. Ini mendengarkan LayoutChanged peristiwa dan Closed sehingga dapat memindahkan atau menghapus hiasan. Ini juga mendengarkan CommentsChanged
peristiwa yang dipicu oleh penyedia hiasan komentar saat komentar ditambahkan atau dihapus.
Tambahkan file kelas ke proyek CommentAdornmentTest dan beri nama
CommentAdornmentManager
.Tambahkan direktif berikut
using
.using System; using System.Collections.Generic; using System.Windows.Media; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting;
Tambahkan kelas bernama
CommentAdornmentManager
.internal class CommentAdornmentManager { }
Tambahkan beberapa bidang privat.
private readonly IWpfTextView view; private readonly IAdornmentLayer layer; private readonly CommentAdornmentProvider provider;
Tambahkan konstruktor yang berlangganan manajer ke LayoutChanged peristiwa dan Closed , dan juga ke
CommentsChanged
acara. Konstruktor bersifat privat karena manajer dibuat oleh metode statisCreate()
.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; }
Create()
Tambahkan metode yang mendapatkan penyedia atau buat jika perlu.public static CommentAdornmentManager Create(IWpfTextView view) { return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentManager>(delegate { return new CommentAdornmentManager(view); }); }
Tambahkan handler
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); }
Tambahkan handler Closed .
private void OnClosed(object sender, EventArgs e) { this.provider.Detach(); this.view.LayoutChanged -= OnLayoutChanged; this.view.Closed -= OnClosed; }
Tambahkan handler 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); } } }
Tambahkan metode privat yang menggambar komentar.
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); } }
Gunakan perintah menu untuk menambahkan hiasan komentar
Anda dapat menggunakan perintah menu untuk membuat hiasan komentar dengan menerapkan MenuItemCallback
metode VSPackage.
Tambahkan referensi berikut ke proyek MenuCommandTest:
Microsoft.VisualStudio.TextManager.Interop
Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Text.UI.Wpf
Buka file AddAdornment.cs dan tambahkan direktif berikut
using
.using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Editor; using CommentAdornmentTest;
Execute()
Hapus metode dan tambahkan handler perintah berikut.private async void AddAdornmentHandler(object sender, EventArgs e) { }
Tambahkan kode untuk mendapatkan tampilan aktif. Anda harus mendapatkan
SVsTextManager
shell Visual Studio untuk mendapatkan yang aktifIVsTextView
.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); }
Jika tampilan teks ini adalah contoh tampilan teks editor, Anda dapat mentransmisikannya ke IVsUserData antarmuka lalu mendapatkan IWpfTextViewHost dan yang terkait IWpfTextView. IWpfTextViewHost Gunakan untuk memanggil
Connector.Execute()
metode , yang mendapatkan penyedia hiasan komentar dan menambahkan hiasan. Handler perintah sekarang akan terlihat seperti kode ini: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); }
Atur metode AddAdornmentHandler sebagai handler untuk perintah AddAdornment di konstruktor AddAdornment.
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); }
Membangun dan menguji kode
Bangun solusi dan mulai penelusuran kesalahan. Instans eksperimental akan muncul.
Buat file teks. Ketik beberapa teks lalu pilih teks tersebut.
Pada menu Alat , klik Panggil Tambahkan Hiasan. Balon harus ditampilkan di sisi kanan jendela teks, dan harus berisi teks yang menyerupai teks berikut.
YourUserName
Fourscore...