Home > Software design >  How to extend a RichTextbox coloring function to color parts of strings?
How to extend a RichTextbox coloring function to color parts of strings?

Time:04-25

private void ColorRichTextbox(RichTextBox box, string text, Color color)
{
    int length = MyRichTextBox.TextLength;
    if (!string.IsNullOrWhiteSpace(MyRichTextBox.Text))
    {
        MyRichTextBox.AppendText("\r\n"   text);
    }
    else
    {
        MyRichTextBox.AppendText(text);
    }
    MyRichTextBox.SelectionStart = length;
    MyRichTextBox.SelectionLength = text.Length;
    MyRichTextBox.SelectionColor = color;
}

Usage:

ColorRichTextbox(MyRichTextBox,
                    "This is example : "   DateTime.Now, Color.Red);

The result is one line in red consisting of "This is example : " with the DateTime.Now part. Is there a way to make for example the part "This is example : " in white color and DateTime.Now to be in red?

CodePudding user response:

You could also do this with a custom Interpolated String Handler.

richTextBox1.AppendColoredLine($"This is an example : {DateTime.Now:red}");
richTextBox1.AppendColoredLine($"This is an example : {DateTime.Now:green}");
richTextBox1.AppendColoredLine($"This is an example : {DateTime.Now:blue}");
richTextBox1.AppendColoredLine($"This is an example : {DateTime.Now:yellow}");

richTextBox1.AppendColoredLine($"Some other colored things: {"hi":orange}, {10:purple}, {this.Name:gray}");

form screenshot

Here's the code to make it work (needs .Net 6):

using System;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace YourNamespace;

public static class RichTextBoxExtensions
{
    [InterpolatedStringHandler]
    [StructLayout(LayoutKind.Auto)]
    public ref struct ColoredStringHandler
    {
        private readonly RichTextBox textBox;
        private readonly IFormatProvider? formatProvider;

        public ColoredStringHandler(int literalLength, int formattedCount, RichTextBox textBox, IFormatProvider? formatProvider = null)
        {
            this.textBox = textBox;
            this.formatProvider = formatProvider;

            textBox.DeselectAll();
        }

        public void AppendLiteral(string s) => textBox.AppendText(s);
        public void AppendFormatted<T>(T item) where T : notnull => AppendLiteral(item.ToString() ?? "");
        public void AppendFormatted<T>(T item, string format) where T : notnull
        {
            var color = Color.FromName(format);
            string? s = 
                item is IFormattable
                ? ((IFormattable)(object)item).ToString(null, formatProvider)
                : item.ToString();

            s ??= "";

            Color previousColor = textBox.SelectionColor;
            try
            {
                textBox.SelectionColor = color;
                textBox.AppendText(s);
            }
            finally
            {
                textBox.SelectionColor = previousColor;
            }
        }
    }

    public static void AppendColored(this RichTextBox textBox, IFormatProvider? formatProvider,
        [InterpolatedStringHandlerArgument("textBox", "formatProvider")] ref ColoredStringHandler handler)
    {

    }

    public static void AppendColored(this RichTextBox textBox,
        [InterpolatedStringHandlerArgument("textBox")] ref ColoredStringHandler handler)
    {

    }

    public static void AppendColoredLine(this RichTextBox textBox, IFormatProvider? formatProvider,
        [InterpolatedStringHandlerArgument("textBox", "formatProvider")] ref ColoredStringHandler handler)
    {
        textBox.AppendText(Environment.NewLine);
    }

    public static void AppendColoredLine(this RichTextBox textBox,
        [InterpolatedStringHandlerArgument("textBox")] ref ColoredStringHandler handler)
    {
        textBox.AppendText(Environment.NewLine);
    }
}

CodePudding user response:

If you modify your method to not always force the text onto its own line, you could call it twice - first for white text and then for red:

private void ColorRichTextbox(RichTextBox box, string text, Color color, bool appendNewLine = true)
{
    int length = box.TextLength;
    if (!string.IsNullOrWhiteSpace(box.Text) && appendNewLine)
    {
        box.AppendText("\r\n"   text);
    }
    else
    {
        box.AppendText(text);
    }
    box.SelectionStart = length;
    box.SelectionLength = text.Length;
    box.SelectionColor = color;
}

By setting appendNewLine to false, the entered text won't be put on a separate line, thus continuing where the previous call left off:

ColorRichTextbox(MyRichTextBox, "This is example : ", Color.White);
ColorRichTextbox(MyRichTextBox, DateTime.Now.ToString(), Color.Red, false); // false = Don't write this text on a separate line

Side note: You don't appear to be using the box argument of your method. If you want to use this for multiple RichTextBoxes, you should replace MyRichTextBox with box in your method (this has been done in my code above).

Result:

Example screenshot

  • Related