Panduan: Sorot teks
Anda dapat menambahkan efek visual yang berbeda ke editor dengan membuat komponen Managed Extensibility Framework (MEF). Panduan ini memperlihatkan cara menyoroti setiap kemunculan kata saat ini dalam file teks. Jika kata terjadi lebih dari satu kali dalam file teks, dan Anda memosisikan tanda sisipan dalam satu kemunculan, setiap kemunculan disorot.
Membuat proyek MEF
Buat proyek C# VSIX. (Dalam Dialog Proyek Baru, pilih Visual C# / Ekstensibilitas, lalu Proyek VSIX.) Beri nama solusi
HighlightWordTest
.Tambahkan templat item Pengklasifikasi Editor ke proyek. Untuk informasi selengkapnya, lihat Membuat ekstensi dengan templat item editor.
Hapus file kelas yang ada.
Menentukan TextMarkerTag
Langkah pertama dalam menyoroti teks adalah dengan subkelas TextMarkerTag dan menentukan tampilannya.
Untuk menentukan TextMarkerTag dan MarkerFormatDefinition
Tambahkan file kelas dan beri nama HighlightWordTag.
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
Presentasi.Core
Presentation.Framework
Impor namespace berikut.
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Threading; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; using System.Windows.Media;
Buat kelas yang mewarisi dan TextMarkerTag beri nama
HighlightWordTag
.internal class HighlightWordTag : TextMarkerTag { }
Buat kelas kedua yang mewarisi dari MarkerFormatDefinition, dan beri nama
HighlightWordFormatDefinition
. Untuk menggunakan definisi format ini untuk tag Anda, Anda harus mengekspornya dengan atribut berikut:NameAttribute: tag menggunakan ini untuk mereferensikan format ini
UserVisibleAttribute: ini menyebabkan format muncul di UI
[Export(typeof(EditorFormatDefinition))] [Name("MarkerFormatDefinition/HighlightWordFormatDefinition")] [UserVisible(true)] internal class HighlightWordFormatDefinition : MarkerFormatDefinition { }
Dalam konstruktor untuk HighlightWordFormatDefinition, tentukan nama tampilan dan tampilannya. Properti Latar Belakang menentukan warna isian, sementara properti Latar Depan menentukan warna batas.
public HighlightWordFormatDefinition() { this.BackgroundColor = Colors.LightBlue; this.ForegroundColor = Colors.DarkBlue; this.DisplayName = "Highlight Word"; this.ZOrder = 5; }
Di konstruktor untuk HighlightWordTag, berikan nama definisi format yang Anda buat.
public HighlightWordTag() : base("MarkerFormatDefinition/HighlightWordFormatDefinition") { }
Menerapkan ITagger
Langkah selanjutnya adalah mengimplementasikan ITagger<T> antarmuka. Antarmuka ini menetapkan, ke buffer teks tertentu, tag yang menyediakan penyorotan teks dan efek visual lainnya.
Untuk mengimplementasikan tagger
Buat kelas yang mengimplementasikan ITagger<T> jenis
HighlightWordTag
, dan beri namaHighlightWordTagger
.internal class HighlightWordTagger : ITagger<HighlightWordTag> { }
Tambahkan bidang dan properti privat berikut ke kelas :
, ITextViewyang sesuai dengan tampilan teks saat ini.
, ITextBufferyang sesuai dengan buffer teks yang mendasar tampilan teks.
, ITextSearchServiceyang digunakan untuk menemukan teks.
, ITextStructureNavigatoryang memiliki metode untuk menavigasi dalam rentang teks.
A NormalizedSnapshotSpanCollection, yang berisi sekumpulan kata yang akan disorot.
A SnapshotSpan, yang sesuai dengan kata saat ini.
A SnapshotPoint, yang sesuai dengan posisi caret saat ini.
Objek kunci.
ITextView View { get; set; } ITextBuffer SourceBuffer { get; set; } ITextSearchService TextSearchService { get; set; } ITextStructureNavigator TextStructureNavigator { get; set; } NormalizedSnapshotSpanCollection WordSpans { get; set; } SnapshotSpan? CurrentWord { get; set; } SnapshotPoint RequestedPoint { get; set; } object updateLock = new object();
Tambahkan konstruktor yang menginisialisasi properti yang tercantum sebelumnya dan menambahkan LayoutChanged dan PositionChanged penanganan aktivitas.
public HighlightWordTagger(ITextView view, ITextBuffer sourceBuffer, ITextSearchService textSearchService, ITextStructureNavigator textStructureNavigator) { this.View = view; this.SourceBuffer = sourceBuffer; this.TextSearchService = textSearchService; this.TextStructureNavigator = textStructureNavigator; this.WordSpans = new NormalizedSnapshotSpanCollection(); this.CurrentWord = null; this.View.Caret.PositionChanged += CaretPositionChanged; this.View.LayoutChanged += ViewLayoutChanged; }
Penanganan aktivitas keduanya memanggil
UpdateAtCaretPosition
metode .void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { // If a new snapshot wasn't generated, then skip this layout if (e.NewSnapshot != e.OldSnapshot) { UpdateAtCaretPosition(View.Caret.Position); } } void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { UpdateAtCaretPosition(e.NewPosition); }
Anda juga harus menambahkan
TagsChanged
peristiwa yang dipanggil oleh metode pembaruan.Metode ini
UpdateAtCaretPosition()
menemukan setiap kata dalam buffer teks yang identik dengan kata di mana kursor diposisikan dan membuat daftar SnapshotSpan objek yang sesuai dengan kemunculan kata. Kemudian memanggilSynchronousUpdate
, yang meningkatkanTagsChanged
peristiwa.void UpdateAtCaretPosition(CaretPosition caretPosition) { SnapshotPoint? point = caretPosition.Point.GetPoint(SourceBuffer, caretPosition.Affinity); if (!point.HasValue) return; // If the new caret position is still within the current word (and on the same snapshot), we don't need to check it if (CurrentWord.HasValue && CurrentWord.Value.Snapshot == View.TextSnapshot && point.Value >= CurrentWord.Value.Start && point.Value <= CurrentWord.Value.End) { return; } RequestedPoint = point.Value; UpdateWordAdornments(); } void UpdateWordAdornments() { SnapshotPoint currentRequest = RequestedPoint; List<SnapshotSpan> wordSpans = new List<SnapshotSpan>(); //Find all words in the buffer like the one the caret is on TextExtent word = TextStructureNavigator.GetExtentOfWord(currentRequest); bool foundWord = true; //If we've selected something not worth highlighting, we might have missed a "word" by a little bit if (!WordExtentIsValid(currentRequest, word)) { //Before we retry, make sure it is worthwhile if (word.Span.Start != currentRequest || currentRequest == currentRequest.GetContainingLine().Start || char.IsWhiteSpace((currentRequest - 1).GetChar())) { foundWord = false; } else { // Try again, one character previous. //If the caret is at the end of a word, pick up the word. word = TextStructureNavigator.GetExtentOfWord(currentRequest - 1); //If the word still isn't valid, we're done if (!WordExtentIsValid(currentRequest, word)) foundWord = false; } } if (!foundWord) { //If we couldn't find a word, clear out the existing markers SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(), null); return; } SnapshotSpan currentWord = word.Span; //If this is the current word, and the caret moved within a word, we're done. if (CurrentWord.HasValue && currentWord == CurrentWord) return; //Find the new spans FindData findData = new FindData(currentWord.GetText(), currentWord.Snapshot); findData.FindOptions = FindOptions.WholeWord | FindOptions.MatchCase; wordSpans.AddRange(TextSearchService.FindAll(findData)); //If another change hasn't happened, do a real update if (currentRequest == RequestedPoint) SynchronousUpdate(currentRequest, new NormalizedSnapshotSpanCollection(wordSpans), currentWord); } static bool WordExtentIsValid(SnapshotPoint currentRequest, TextExtent word) { return word.IsSignificant && currentRequest.Snapshot.GetText(word.Span).Any(c => char.IsLetter(c)); }
melakukan
SynchronousUpdate
pembaruan sinkron padaWordSpans
properti danCurrentWord
, dan menaikkanTagsChanged
peristiwa.void SynchronousUpdate(SnapshotPoint currentRequest, NormalizedSnapshotSpanCollection newSpans, SnapshotSpan? newCurrentWord) { lock (updateLock) { if (currentRequest != RequestedPoint) return; WordSpans = newSpans; CurrentWord = newCurrentWord; var tempEvent = TagsChanged; if (tempEvent != null) tempEvent(this, new SnapshotSpanEventArgs(new SnapshotSpan(SourceBuffer.CurrentSnapshot, 0, SourceBuffer.CurrentSnapshot.Length))); } }
Anda harus menerapkan metode .GetTags Metode ini mengambil kumpulan SnapshotSpan objek dan mengembalikan enumerasi rentang tag.
Dalam C#, terapkan metode ini sebagai iterator hasil, yang memungkinkan evaluasi malas (yaitu, evaluasi dari set hanya ketika item individual diakses) dari tag. Di Visual Basic, tambahkan tag ke daftar dan kembalikan daftar.
Di sini metode mengembalikan TagSpan<T> objek yang memiliki "biru" TextMarkerTag, yang menyediakan latar belakang biru.
public IEnumerable<ITagSpan<HighlightWordTag>> GetTags(NormalizedSnapshotSpanCollection spans) { if (CurrentWord == null) yield break; // Hold on to a "snapshot" of the word spans and current word, so that we maintain the same // collection throughout SnapshotSpan currentWord = CurrentWord.Value; NormalizedSnapshotSpanCollection wordSpans = WordSpans; if (spans.Count == 0 || wordSpans.Count == 0) yield break; // If the requested snapshot isn't the same as the one our words are on, translate our spans to the expected snapshot if (spans[0].Snapshot != wordSpans[0].Snapshot) { wordSpans = new NormalizedSnapshotSpanCollection( wordSpans.Select(span => span.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive))); currentWord = currentWord.TranslateTo(spans[0].Snapshot, SpanTrackingMode.EdgeExclusive); } // First, yield back the word the cursor is under (if it overlaps) // Note that we'll yield back the same word again in the wordspans collection; // the duplication here is expected. if (spans.OverlapsWith(new NormalizedSnapshotSpanCollection(currentWord))) yield return new TagSpan<HighlightWordTag>(currentWord, new HighlightWordTag()); // Second, yield all the other words in the file foreach (SnapshotSpan span in NormalizedSnapshotSpanCollection.Overlap(spans, wordSpans)) { yield return new TagSpan<HighlightWordTag>(span, new HighlightWordTag()); } }
Membuat penyedia Tagger
Untuk membuat tagger, Anda harus menerapkan IViewTaggerProvider. Kelas ini adalah bagian komponen MEF, jadi Anda harus mengatur atribut yang benar sehingga ekstensi ini dikenali.
Catatan
Untuk mendapatkan informasi selengkapnya tentang MEF, lihat Managed Extensibility Framework (MEF).
Untuk membuat penyedia tagger
Buat kelas bernama
HighlightWordTaggerProvider
yang mengimplementasikan IViewTaggerProvider, dan ekspor dengan ContentTypeAttribute "teks" dan TagTypeAttribute dari TextMarkerTag.[Export(typeof(IViewTaggerProvider))] [ContentType("text")] [TagType(typeof(TextMarkerTag))] internal class HighlightWordTaggerProvider : IViewTaggerProvider { }
Anda harus mengimpor dua layanan editor, ITextSearchService dan ITextStructureNavigatorSelectorService, untuk membuat instans tagger.
[Import] internal ITextSearchService TextSearchService { get; set; } [Import] internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelector { get; set; }
Terapkan CreateTagger metode untuk mengembalikan instans
HighlightWordTagger
.public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag { //provide highlighting only on the top buffer if (textView.TextBuffer != buffer) return null; ITextStructureNavigator textStructureNavigator = TextStructureNavigatorSelector.GetTextStructureNavigator(buffer); return new HighlightWordTagger(textView, buffer, TextSearchService, textStructureNavigator) as ITagger<T>; }
Membangun dan menguji kode
Untuk menguji kode ini, buat solusi HighlightWordTest dan jalankan dalam instans eksperimental.
Untuk membangun dan menguji solusi HighlightWordTest
Bangun solusinya.
Saat Anda menjalankan proyek ini di debugger, instans kedua Visual Studio dimulai.
Buat file teks dan ketik beberapa teks di mana kata-kata diulang, misalnya, "halo halo halo".
Posisikan kursor di salah satu kemunculan "halo". Setiap kemunculan harus disorot dengan warna biru.