0

I am using TextRanges in a WPF RichTextBox and I want to change the font color of a TextRange, but that if then it is written text next to it, it has the original color. For exmaple, I have a RichTextBox called richTextBox with font color black, and I use this code:

TextPointer start = richTextBox.Document.ContentStart;
TextPointer end = richTextBox.Document.ContentEnd;
new TextRange(start, end).ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Blue);

I want that if, after executing this code, someone adds text to the richTextBox, it is written in black, not in blue. Is there any way to do this? Thank you!

slugster
  • 49,403
  • 14
  • 95
  • 145

2 Answers2

0

I don't think you can set it directly so that the next caracter would be in a different color. (Someone please correct me if you know how).

But what you could do is subscribe to the TextChangedEvent of your RichTextBox and apply the new foreground color when the user add some text at the end.

The tricky part however is to detect how much was changed so you can apply the new foreground to this part only and to know if the changes made where done at the end of the text.

Where you execute the code for setting the foreground to blue:

  TextPointer start = this.RTextBox.Document.ContentStart;
  TextPointer end = this.RTextBox.Document.ContentEnd;
  TextRange textRange = new TextRange(start, end);
  textRange.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Blue);
  //Storing the text to allow for comparison later
  this.textSaved = textRange.Text;
  this.wasTextColorInitialized = true;

Variables and new method added:

    private bool wasTextColorInitialized = false;

    private string textSaved;

    /// <summary>
    /// Remove some of the invisible caracter in a string to allowe for comparison
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
    private string GetComparableString(string text)
    {
        return string.Join(string.Empty, Regex.Split(text, @"(?:\r\n|\n|\r)"));
    }

Source for the very useful regex expression https://stackoverflow.com/a/1982317/13448212

In the textChanged event, you have to check if the change is due to the user adding text, then determine if the changes were made at the end of the text:

private void RTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextPointer endOfDoc = this.RTextBox.Document.ContentEnd;
        TextPointer startOfDoc = this.RTextBox.Document.ContentStart;
        TextRange fullRange = new TextRange(startOfDoc, endOfDoc);

        string newText = fullRange.Text;

        //e.Changes.Count = 0 means that only a Property was changed (eg Foreground)
        if (this.wasTextColorInitialized && e.Changes.Count != 0)
        {
            int lengthOfTextAdded = newText.Length - this.textSaved.Length;
            //The text can end with "\r\n" and the text added will be before this hence the "GetComparableString" method
            if (lengthOfTextAdded > 0 && newText.StartsWith(this.GetComparableString(this.textSaved)))
            {
                //in e.Changes, the changes are ordered, the latest being the furthest in the text
                TextRange rangeOfTextAdded = new TextRange(endOfDoc.GetPositionAtOffset(-e.Changes.Last().AddedLength), endOfDoc);
                rangeOfTextAdded.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Black);
            }
            else
            {
                //Nothing to do
            }
        }
        else
        {
            //Nothing to do
        }
        this.textSaved = newText;
    }

Documentation for e.Changes: https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.textchangedeventargs.changes?view=netcore-3.1#System_Windows_Controls_TextChangedEventArgs_Changes

Ostas
  • 839
  • 7
  • 11
0

I think Ostas's approach will work. I just would like to suggest a simple workaround.

It is to add a space at the end of RichTextBox when changing the color. After that, if someone adds a new text immediately after the existing text, the color will be blue. If someone adds a new text at the end (after the space), the color will be default.

TextPointer start = textBox.Document.ContentStart;
TextPointer end = textBox.Document.ContentEnd;

var range = new TextRange(start, end);
range.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Blue);

if (textBox.Document.Blocks.LastBlock is Paragraph paragraph)
    paragraph.Inlines.Add(" ");
emoacht
  • 2,764
  • 1
  • 13
  • 24