ion
ion

Reputation: 79

Highlight bracketed parts of RichTextBox in C#

I need a way to dynamically (on TextChanged event) highlight the bracketed parts of a RichTextBox, but i'm stuck. For example, if the text is:

"This is a {very nice} and {peaceful} morning"

the parts "{very nice}" and "{peacefull}" (including brackets) should get a different text color.

Upvotes: 0

Views: 857

Answers (1)

csharpfolk
csharpfolk

Reputation: 4280

What you probably looking for is RichTextBox with syntax highlighting (what you try to highlight is similar to string highlighting in code editors). There are many questions on stackoverflow with good answers already, e.g. A textbox/richtextbox that has syntax highlighting? [C#]. If you must use RichTextBox you can start with the following code:

private void rtb_TextChanged(object sender, EventArgs e) {
        var cursorPosition = rtb.SelectionStart;

        rtb.SelectAll();
        rtb.SelectionColor = Color.Black;

        var partsToHighlight = Regex.Matches(rtb.Text, "{[^}{]*}")
            .Cast<Match>()
            .ToList();

        foreach (var part in partsToHighlight) {
            rtb.Select(part.Index, part.Length);
            rtb.SelectionColor = Color.Red;
        }

        rtb.Select(cursorPosition, 0);
    }

Unfortunately it causes flicker, loses scroll bar position and can take some time when processing huge blocks of text. Better approach is to use some custom control e.g. Scintilla:

public partial class Form1 : Form {
    private readonly CustomLexer _lexer = new CustomLexer();

    public Form1() {
        InitializeComponent();

        var editor = new ScintillaNET.Scintilla {
            Dock = DockStyle.Fill,

        };
        this.Controls.Add(editor);

        editor.StyleResetDefault();
        editor.Styles[Style.Default].Font = "Consolas";
        editor.Styles[Style.Default].Size = 10;
        editor.StyleClearAll();

        editor.Styles[CustomLexer.StyleText].ForeColor = Color.Black;
        editor.Styles[CustomLexer.StyleParens].ForeColor = Color.Red;


        editor.Lexer = Lexer.Container;
        editor.StyleNeeded += scintilla_StyleNeeded;
    }

    private void scintilla_StyleNeeded(object sender, StyleNeededEventArgs e) {
        var scintilla = (ScintillaNET.Scintilla)sender;

        var startPos = scintilla.GetEndStyled();
        var endPos = e.Position;

        _lexer.Style(scintilla, startPos, endPos);
    }
}

public class CustomLexer {
    public const int StyleText = 0;
    public const int StyleParens = 1;

    private const int STATE_TEXT = 0;
    private const int STATE_INSIDE_PARENS = 1;

    public void Style(Scintilla scintilla, int startPosition, int endPosition) {
        // Back up to the line start

        var startLine = scintilla.LineFromPosition(startPosition);
        var endLine = scintilla.LineFromPosition(endPosition);

        var fixedStartPosition = scintilla.Lines[startLine].Position;
        var fixedStopPosition = scintilla.Lines[endLine].Position + scintilla.Lines[endLine].Length;

        scintilla.StartStyling(fixedStartPosition);

        bool insideBrackets = false;
        for (var i = fixedStartPosition; i < fixedStopPosition; i++) {
            var c = (char)scintilla.GetCharAt(i);

            if (c == '{') insideBrackets = true;

            if (insideBrackets) {
                scintilla.SetStyling(1, StyleParens);
            }
            else {
                scintilla.SetStyling(1, StyleText);
            }

            if (c == '}') insideBrackets = false;
        }
    }
}

This is based on a great tutorial here.

Upvotes: 1

Related Questions