0

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.

ion
  • 79
  • 2
  • 10
  • If brackets may be nested then this is not a trivial problem, and you will need to parse entire text in RTB. In non nested case quick solution is wait until user enter `}` character, then find matching `{` character, select range of text based on these character indexes and change selected text attributes (e.g. color - https://msdn.microsoft.com/en-us/library/system.windows.forms.richtextbox.selectioncolor(v=vs.110).aspx) – csharpfolk Dec 13 '15 at 21:17
  • No, the brackets cannot be nested. To check the last character entered is a nice approach. But if the user deletes a random bracket? Maybe i should parse the whole text on every keystroke to reapply the formatting. – ion Dec 13 '15 at 22:19

1 Answers1

1

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.

Community
  • 1
  • 1
csharpfolk
  • 4,124
  • 25
  • 31