Color different parts of a RichTextBox string

Viewed 194505

I'm trying to color parts of a string to be appended to a RichTextBox. I have a string built from different strings.

string temp = "[" + DateTime.Now.ToShortTimeString() + "] " +
              userid + " " + message + Environment.NewLine;

This is what the message would look like once it is constructed.

[9:23pm] User: my message here.

I want everything within and including the brackets [9:23] to be one color, 'user' to be another color and the message to be another color. Then I'd like the string appended to my RichTextBox.

How can I accomplish this?

10 Answers

Here is an extension method that overloads the AppendText method with a color parameter:

public static class RichTextBoxExtensions
{
    public static void AppendText(this RichTextBox box, string text, Color color)
    {
        box.SelectionStart = box.TextLength;
        box.SelectionLength = 0;

        box.SelectionColor = color;
        box.AppendText(text);
        box.SelectionColor = box.ForeColor;
    }
}

And this is how you would use it:

var userid = "USER0001";
var message = "Access denied";
var box = new RichTextBox
              {
                  Dock = DockStyle.Fill,
                  Font = new Font("Courier New", 10)
              };

box.AppendText("[" + DateTime.Now.ToShortTimeString() + "]", Color.Red);
box.AppendText(" ");
box.AppendText(userid, Color.Green);
box.AppendText(": ");
box.AppendText(message, Color.Blue);
box.AppendText(Environment.NewLine);

new Form {Controls = {box}}.ShowDialog();

Note that you may notice some flickering if you're outputting a lot of messages. See this C# Corner article for ideas on how to reduce RichTextBox flicker.

It`s work for me! I hope it will be useful to you!

public static RichTextBox RichTextBoxChangeWordColor(ref RichTextBox rtb, string startWord, string endWord, Color color)
{
    rtb.SuspendLayout();
    Point scroll = rtb.AutoScrollOffset;
    int slct = rtb.SelectionIndent;
    int ss = rtb.SelectionStart;
    List<Point> ls = GetAllWordsIndecesBetween(rtb.Text, startWord, endWord, true);
    foreach (var item in ls)
    {
        rtb.SelectionStart = item.X;
        rtb.SelectionLength = item.Y - item.X;
        rtb.SelectionColor = color;
    }
    rtb.SelectionStart = ss;
    rtb.SelectionIndent = slct;
    rtb.AutoScrollOffset = scroll;
    rtb.ResumeLayout(true);
    return rtb;
}

public static List<Point> GetAllWordsIndecesBetween(string intoText, string fromThis, string toThis,bool withSigns = true)
{
    List<Point> result = new List<Point>();
    Stack<int> stack = new Stack<int>();
    bool start = false;
    for (int i = 0; i < intoText.Length; i++)
    {
        string ssubstr = intoText.Substring(i);
        if (ssubstr.StartsWith(fromThis) && ((fromThis == toThis && !start) || !ssubstr.StartsWith(toThis)))
        {
            if (!withSigns) i += fromThis.Length;
            start = true;
            stack.Push(i);
        }
        else if (ssubstr.StartsWith(toThis) )
        {
            if (withSigns) i += toThis.Length;
            start = false;
            if (stack.Count > 0)
            {
                int startindex = stack.Pop();
                result.Add(new Point(startindex,i));
            }
        }
    }
    return result;
}

I created this Function after researching on the internet since I wanted to print an XML string when you select a row from a data grid view.

static void HighlightPhrase(RichTextBox box, string StartTag, string EndTag, string ControlTag, Color color1, Color color2)
{
    int pos = box.SelectionStart;
    string s = box.Text;
    for (int ix = 0; ; )
    {
        int jx = s.IndexOf(StartTag, ix, StringComparison.CurrentCultureIgnoreCase);
        if (jx < 0) break;
        int ex = s.IndexOf(EndTag, ix, StringComparison.CurrentCultureIgnoreCase);
        box.SelectionStart = jx;
        box.SelectionLength = ex - jx + 1;
        box.SelectionColor = color1;
        
        int bx = s.IndexOf(ControlTag, ix, StringComparison.CurrentCultureIgnoreCase);
        int bxtest = s.IndexOf(StartTag, (ex + 1), StringComparison.CurrentCultureIgnoreCase);
        if (bx == bxtest)
        {
            box.SelectionStart = ex + 1;
            box.SelectionLength = bx - ex + 1;
            box.SelectionColor = color2;
        }
        
        ix = ex + 1;
    }
    box.SelectionStart = pos;
    box.SelectionLength = 0;
}

and this is how you call it

   HighlightPhrase(richTextBox1, "<", ">","</", Color.Red, Color.Black);

Using Selection in WPF, aggregating from several other answers, no other code is required (except Severity enum and GetSeverityColor function)

 public void Log(string msg, Severity severity = Severity.Info)
    {
        string ts = "[" + DateTime.Now.ToString("HH:mm:ss") + "] ";
        string msg2 = ts + msg + "\n";
        richTextBox.AppendText(msg2);

        if (severity > Severity.Info)
        {
            int nlcount = msg2.ToCharArray().Count(a => a == '\n');
            int len = msg2.Length + 3 * (nlcount)+2; //newlines are longer, this formula works fine
            TextPointer myTextPointer1 = richTextBox.Document.ContentEnd.GetPositionAtOffset(-len);
            TextPointer myTextPointer2 = richTextBox.Document.ContentEnd.GetPositionAtOffset(-1);

            richTextBox.Selection.Select(myTextPointer1,myTextPointer2);
            SolidColorBrush scb = new SolidColorBrush(GetSeverityColor(severity));
            richTextBox.Selection.ApplyPropertyValue(TextElement.BackgroundProperty, scb);

        }

        richTextBox.ScrollToEnd();
    }

I prepared a little helper for the RichTextBox control which makes it very easy to generate colored text on the screen:

using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Common.Helpers
{
    public class ColouredText
    {
        public string Text;
        public Color Foreground;
        public Color Background;

        public ColouredText(string text, Color foreground, Color background)
        {
            Text = text;
            Foreground = foreground;
            Background = background;
        }

        public ColouredText(string text, Color foreground) : this(text, foreground, Color.Transparent) { }

        public ColouredText(string text) : this(text, Color.Transparent, Color.Transparent) { }
    }

    public static class RichTextBoxHelper
    {
        private static RichTextBox _AppendText(RichTextBox box, string text, Color foreColor, Color backColor)
        {
            if (string.IsNullOrEmpty(text)) return box;

            box.SelectionStart = box.TextLength;
            box.SelectionLength = 0;

            box.SelectionColor = foreColor;
            box.SelectionBackColor = backColor;
            box.AppendText(text);
            box.SelectionColor = box.ForeColor;

            return box;
        }

        private static void _UpdateText(RichTextBox box, IEnumerable<ColouredText> newTextWithColors)
        {
            box.Text = "";

            foreach (var text in newTextWithColors)
            {
                var foreColor = text.Foreground; if (foreColor == Color.Transparent) foreColor = box.ForeColor;
                var backColor = text.Background; if (backColor == Color.Transparent) backColor = box.BackColor;

                _AppendText(box, text.Text, foreColor, backColor);
            }
        }

        public static void UpdateText(this RichTextBox richTextbox, IEnumerable<ColouredText> text)
        {
            if (richTextbox.InvokeRequired) richTextbox.Invoke((MethodInvoker)(() => { _UpdateText(richTextbox, text); }));
            else _UpdateText(richTextbox, text);
        }

        public static void UpdateText(this RichTextBox richTextbox, ColouredText text)
        {
            var list = new List<ColouredText>() { text };

            if (richTextbox.InvokeRequired) richTextbox.Invoke((MethodInvoker)(() => { _UpdateText(richTextbox, list); }));
            else _UpdateText(richTextbox, list);
        }
    }
}

and now you can use:

var text = new List<ColouredText>()
{
    new ColouredText($"text#1    ", Color.Black),
    new ColouredText($"text#2    ", Color.Red, Color.Yellow),
    new ColouredText($" "),
    new ColouredText($"text#2    ", Color.White, Color.Black)
};

richTextBox1.UpdateText(text);

or simpler usage for single-line text:

richTextBox1.UpdateText(new ColouredText($"warning message", Color.Yellow, Color.Red));
Related