Pewnie wielu z Was spotkało się kiedyś z kontrolką RichTextBox, w której najprościej jest wyświetlić jakiś sformatowany dokument jak na przykład rtf.
Jakiś czas temu dostałem zadanie, które polegało na tym, że użytkownik wpisywał teść w polu z ograniczonym rozmiarem znaków. Jeżeli ilość znaków zostałaby przekroczona, tekst, który jest nadmiarowy miał zmienić kolor na inny niż czarny.
Sprawa nie była zbyt prosta jak się okazało ponieważ sam RichTextBox jest już problemem. Kontrolka nie posiada bezpośredniego bindingu do wpisanego tekstu. Spowodowane jest to przez to, że istnieje wiele rodzajów obiektów, które mogą być dziećmi tego komponentu wiec potrzebny jest workaround:
|
1 2 3 4 5 6 7 8 |
public static class RichTextBoxExtension { public static string Text(this RichTextBox richTextBox) { TextRange content = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd); return content.Text; } } |
Napisałem extension method, które podaje mi wpisany tekst w polu.
Kolejną rzeczą, która jest potrzebna to informacja o tekście, któremu mam zmienić kolor. Do tego napisałem metodę, która zwraca mi obiekt typu TextPointer, który jest czymś w rodzaju wskaźnika.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public TextPointer GetPointerFromCharOffset(int charOffset, TextPointer startPointer, FlowDocument document) { TextPointer startNavigate = startPointer; if (charOffset == 0) { return startNavigate; } TextPointer nextPointer = startNavigate; var counter = 0; while (nextPointer != null && counter <= charOffset) { if (nextPointer.CompareTo(document.ContentEnd) == 0) { return nextPointer; } nextPointer = nextPointer.GetNextInsertionPosition(LogicalDirection.Forward); counter++; } return nextPointer; } |
Dzięki temu mogę w tym momencie sprawdzić jak długi jest wpisywany tekst i jeżeli tekst będzie większy od jego maksymalnego rozmiaru mogę pobrać informację od którego, do którego miejsca mam zmienić kolor.
|
1 2 3 |
TextRange range = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd); TextPointer start = GetPointerFromCharOffset(index, range.Start, richTextBox.Document); TextPointer end = GetPointerFromCharOffset(length + index, range.End, richTextBox.Document); |
3 linikji powyżej umożliwiają mi właśnie taki scenariusz.
W zmiennej range przechowywana jest wartość całego wpisanego teksu, start i end to nic innego jak zmienne, które mówią o tym od którego do którego miejsca mam zmienić kolor tekstu
Aby te 3 linijki grały ze sobą stworzyłem Behavior dla RichTextBox gdzie zapiąłem się na event TextChanged gdzie sprawdzam moje warunki i gdy trzeba zaczynam kolorować tekst na inny kolor
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private void AssociatedObjectOnTextChanged(object sender, TextChangedEventArgs textChangedEventArgs) { AssociatedObject.TextChanged -= AssociatedObjectOnTextChanged; var richTextBox = sender as RichTextBox; if (richTextBox != null) { <strong>TextRange range = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);</strong> range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Black)); if (MaxTextLength - richTextBox.Text().Length < 0) { var enterCount = richTextBox.Text().Count(c => c == '\r'); int index = 98 - enterCount; int length = _changeIndex += textChangedEventArgs.Changes.Count; <strong> TextPointer start = GetPointerFromCharOffset(index, range.Start, richTextBox.Document); TextPointer end = GetPointerFromCharOffset(length + index, range.End, richTextBox.Document);</strong> if (start != null && end != null) { TextRange tr = new TextRange(start, end); tr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Red)); } } TextCounter = richTextBox.Text().Length.ToString(); } AssociatedObject.TextChanged += AssociatedObjectOnTextChanged; } |
W całym moim obliczaniu są 2 najważniejsze rzeczy:
- Entery – trzeba pamiętać, że enter nie jest brany pod uwagę jeżeli tekst kolorujemy ale jest brany pod uwagę jeżeli liczymy znaki.
Nie pytajcie dlaczego tak jest bo odpowiedź jest jedna… tak ma być! - Wprowadzenie pierwszego znaku do RichTextBox powoduje, że automatycznie dodaje się na końcu enter i nie mam pojęcia jak to wyłączyć
Moim założeniem jest wprowadzenie 100 znaków, 101 ma być koloru czerwonego, 98 jest dlatego ponieważ od 100 muszę odjąć jeszcze ten nieszczęsny enter, który jest dodawany automatycznie.

