İzlenecek yol: Metni vurgulama
Yönetilen Genişletilebilirlik Çerçevesi (MEF) bileşen parçaları oluşturarak düzenleyiciye farklı görsel efektler ekleyebilirsiniz. Bu izlenecek yol, geçerli sözcüğün bir metin dosyasındaki her geçtiği yeri vurgulama işlemini gösterir. Metin dosyasında bir sözcük birden çok kez oluşuyorsa ve şapka işaretini tek bir oluşumda konumlandırıyorsanız, her oluşum vurgulanır.
MEF projesi oluşturma
C# VSIX projesi oluşturun. (Yeni Proje iletişim kutusu, Visual C# / Genişletilebilirlik'i ve ardından VSIX Projesi'ni seçin.) Çözümü
HighlightWordTest
olarak adlandırın.Projeye bir Düzenleyici Sınıflandırıcısı öğesi şablonu ekleyin. Daha fazla bilgi için bkz . Düzenleyici öğesi şablonuyla uzantı oluşturma.
Varolan sınıf dosyalarını silin.
TextMarkerTag Tanımlama
Metni vurgulamanın ilk adımı alt sınıfa TextMarkerTag geçmek ve görünümünü tanımlamaktır.
TextMarkerTag ve MarkerFormatDefinition tanımlamak için
Sınıf dosyasını ekleyin ve HighlightWordTag olarak adlandırnın.
Aşağıdaki başvuruları ekleyin:
Microsoft.VisualStudio.CoreUtility
Microsoft.VisualStudio.Text.Data
Microsoft.VisualStudio.Text.Logic
Microsoft.VisualStudio.Text.UI
Microsoft.VisualStudio.Text.UI.Wpf
System.ComponentModel.Composition
Presentation.Core
Presentation.Framework
Aşağıdaki ad alanlarını içeri aktarın.
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;
öğesinden TextMarkerTag devralan bir sınıf oluşturun ve olarak adlandırın
HighlightWordTag
.internal class HighlightWordTag : TextMarkerTag { }
öğesinden MarkerFormatDefinitiondevralan ikinci bir sınıf oluşturun ve olarak adlandırın
HighlightWordFormatDefinition
. Etiketinizde bu biçim tanımını kullanmak için aşağıdaki özniteliklerle dışarı aktarmanız gerekir:NameAttribute: etiketler bu biçime başvurmak için bunu kullanır
UserVisibleAttribute: bu, biçimin kullanıcı arabiriminde görünmesine neden olur
[Export(typeof(EditorFormatDefinition))] [Name("MarkerFormatDefinition/HighlightWordFormatDefinition")] [UserVisible(true)] internal class HighlightWordFormatDefinition : MarkerFormatDefinition { }
HighlightWordFormatDefinition oluşturucusunda görünen adını ve görünümünü tanımlayın. Background özelliği dolgu rengini tanımlarken Foreground özelliği kenarlık rengini tanımlar.
public HighlightWordFormatDefinition() { this.BackgroundColor = Colors.LightBlue; this.ForegroundColor = Colors.DarkBlue; this.DisplayName = "Highlight Word"; this.ZOrder = 5; }
HighlightWordTag oluşturucusunda, oluşturduğunuz biçim tanımının adını geçirin.
public HighlightWordTag() : base("MarkerFormatDefinition/HighlightWordFormatDefinition") { }
ITagger uygulama
Sonraki adım arabirimini uygulamaktır ITagger<T> . Bu arabirim, belirli bir metin arabelleğine, metin vurgulama sağlayan etiketlere ve diğer görsel efektlere atar.
Bir etiketleme uygulamak için
türünde uygulayan ITagger<T> bir sınıf oluşturun ve olarak adlandırın
HighlightWordTagger
.HighlightWordTag
internal class HighlightWordTagger : ITagger<HighlightWordTag> { }
Sınıfına aşağıdaki özel alanları ve özellikleri ekleyin:
Geçerli metin görünümüne karşılık gelen bir ITextView.
Metin ITextBuffergörünümünün altında yer alan metin arabelleğine karşılık gelen bir .
ITextSearchServiceMetin bulmak için kullanılan bir .
Metin aralıkları içinde gezinme yöntemlerine sahip olan bir ITextStructureNavigator.
NormalizedSnapshotSpanCollectionVurgulanan sözcük kümesini içeren bir .
SnapshotSpanGeçerli sözcüye karşılık gelen bir .
Şapka SnapshotPointişaretinin geçerli konumuna karşılık gelen bir .
Bir kilit nesnesi.
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();
Daha önce listelenen özellikleri başlatan ve ile PositionChanged olay işleyicileri ekleyen LayoutChanged bir oluşturucu ekleyin.
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; }
Olay işleyicilerinin her ikisi de yöntemini çağırır
UpdateAtCaretPosition
.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); }
Güncelleştirme yöntemi tarafından çağrılan bir
TagsChanged
olay da eklemeniz gerekir.yöntemi,
UpdateAtCaretPosition()
metin arabelleğindeki imlecin konumlandırıldığı sözcükle aynı olan her sözcüğü bulur ve sözcüğün SnapshotSpan oluşumlarına karşılık gelen nesnelerin listesini oluşturur. Ardından, olayı tetikleyenTagsChanged
öğesini çağırırSynchronousUpdate
.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)); }
ve
SynchronousUpdate
CurrentWord
özelliklerindeWordSpans
zaman uyumlu bir güncelleştirme gerçekleştirir ve olayı tetiklerTagsChanged
.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))); } }
yöntemini uygulamanız GetTags gerekir. Bu yöntem bir nesne koleksiyonu SnapshotSpan alır ve etiket yayılmalarının bir numaralandırması döndürür.
C# dilinde bu yöntemi, etiketlerin gecikmeli değerlendirmesini (yani yalnızca tek tek öğelere erişildiğinde kümenin değerlendirilmesini) sağlayan bir verim yineleyicisi olarak uygulayın. Visual Basic'te etiketleri bir listeye ekleyin ve listeyi döndürün.
Burada yöntemi, mavi arka TagSpan<T> plan sağlayan "mavi" TextMarkerTagolan bir nesne döndürür.
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()); } }
Tagger sağlayıcısı oluşturma
Etiketleyicinizi oluşturmak için bir IViewTaggerProvideruygulamanız gerekir. Bu sınıf bir MEF bileşeni bölümüdür, bu nedenle bu uzantının tanınması için doğru öznitelikleri ayarlamanız gerekir.
Not
MEF hakkında daha fazla bilgi için bkz . Yönetilen Genişletilebilirlik Çerçevesi (MEF).
Bir etiketleme sağlayıcısı oluşturmak için
uygulayan adlı
HighlightWordTaggerProvider
bir sınıf oluşturun ve bunu bir "text" ve bir TagTypeAttribute öğesiyle dışarı ContentTypeAttribute aktarınTextMarkerTag.IViewTaggerProvider[Export(typeof(IViewTaggerProvider))] [ContentType("text")] [TagType(typeof(TextMarkerTag))] internal class HighlightWordTaggerProvider : IViewTaggerProvider { }
Tagger örneği için ve ITextStructureNavigatorSelectorServiceolmak üzere iki düzenleyici hizmetini ITextSearchService içeri aktarmanız gerekir.
[Import] internal ITextSearchService TextSearchService { get; set; } [Import] internal ITextStructureNavigatorSelectorService TextStructureNavigatorSelector { get; set; }
bir örneğini CreateTagger
HighlightWordTagger
döndürmek için yöntemini uygulayın.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>; }
Kodu derleme ve test etme
Bu kodu test etmek için HighlightWordTest çözümünü derleyin ve deneysel örnekte çalıştırın.
HighlightWordTest çözümünü derlemek ve test etmek için
Çözümü oluşturun.
Bu projeyi hata ayıklayıcıda çalıştırdığınızda, Visual Studio'nun ikinci bir örneği başlatılır.
Bir metin dosyası oluşturun ve sözcüklerin yinelendiği bir metin yazın; örneğin, "merhaba merhaba merhaba".
İmleci "hello" örneğinden birine getirin. Her oluşum mavi renkle vurgulanmalıdır.