1

I am writing a c# Html editor application, in which you type the code in a RichTextBox control. I want the RichTextBox to behave like notepad++ and other code editors in which the Html syntax gets highlighted in colors, like this for example:

Example

How can I establish this in C# windows form RichTextBox? I have searched almost everywhere and didn't find anything that helped me. This is what I tried so far but I doesn't give the result I want:

private void SyntaxHighlight()
        {
            string[] tags = { "html","head","body","a","b","img","strong","p","h1","h2","h3","h4","h5","h6","embed","iframe","span","form",
                            "button","input","textarea","br","div","style","script","table","tr","td","th","i","u","link","meta","title"};
            foreach (string s in tags)
            {
                richTextBox1.Find("<" + s); 
                richTextBox1.SelectionColor = Color.Blue;
                richTextBox1.Find(">");
                richTextBox1.SelectionColor = Color.Blue;
            }

            string[] attributes = { "href","src","height","width","rowspan","colspan","target","style","onclick","id","name","class"};
            foreach (string s in attributes)
            {
                richTextBox1.Find(s + "=");
                richTextBox1.SelectionColor = Color.Red;
            }
        }

Can someone help me? What should I write inside the SyntaxHighlight() method? can someone give me the appropriate code? Thank you

Angel
  • 146
  • 2
  • 11
  • Have you checked out [this](http://stackoverflow.com/questions/11183599/rich-text-box-how-to-highlight-text-block) question and its solution? – fujiFX Apr 20 '16 at 05:11
  • 1
    @fujiFX yes but that's not what I want, I want to highlight the text not the background – Angel Apr 20 '16 at 05:13
  • What is the result you are getting with your provided code? – fujiFX Apr 20 '16 at 05:16

3 Answers3

4

Within your code you are only finding the 1st occurrence of the HTML tag and highlighting it. But instead, you should loop through the entire rich text content to finding proceeding occurrences of the same text. I just did a quick mock based on your exact code, please check it out.

    private void highlightHTMLText()
    {
        string[] tags = { "html","head","body","a","b","img","strong","p","h1","h2","h3","h4","h5","h6","embed","iframe","span","form",
                        "button","input","textarea","br","div","style","script","table","tr","td","th","i","u","link","meta","title"};
        foreach (string s in tags)
        {
            findAndHighlight("<" + s, Color.Blue);
            findAndHighlight("</" + s, Color.Blue);
            findAndHighlight(">", Color.Blue);
        }

        string[] attributes = { "href", "src", "height", "width", "rowspan", "colspan", "target", "style", "onclick", "id", "name", "class" };
        foreach (string s in attributes)
        {
            findAndHighlight(s + "=", Color.Red);
        }
    }

    private void findAndHighlight(string sSearchStr, Color oColor)
    {
        int index = richTextBox1.Text.IndexOf(sSearchStr);
        while (index != -1)
        {
            richTextBox1.Select(index, sSearchStr.Length);
            richTextBox1.SelectionColor = oColor;

            index = richTextBox1.Text.IndexOf(sSearchStr, index + sSearchStr.Length);
        }
    }

Further as per this answer you should be able to make use of the same utility library Scintilla used by Notepad++ itself. As pointed out you do not need to re-invent the wheel, but as a developer I obviously prefer my own util (it is just me ;) ). Hope this helps.

fujiFX
  • 413
  • 9
  • 18
  • Thank you. I am going to try your code now. as for Scintilla I don't know how to import it into my project. can you tell me how? – Angel Apr 20 '16 at 06:44
  • I myself haven't used Scintilla as well. Just went through details provided in their CodPlex page and it seems that we should be able to make use the provided Scintilla controller instead of the `RichTextBox`. Please have read on [this page](http://scintillanet.codeplex.com/wikipage?title=ScintillaNET%20Types&referringTitle=Documentation) as well as [Scintilla Documentation](http://www.scintilla.org/ScintillaDoc.html) on their parent site. – fujiFX Apr 20 '16 at 06:58
0

Find doesn't move a cursor, it returns the location of the first match. Try this instead:

How to select text from the RichTextBox and then color it?

Community
  • 1
  • 1
tim
  • 114
  • 1
0

A little late to the party but, after wanting to create my own offline version of CodePen, I implemented my own version of html syntax highlighting following CodePen's theme.

This does syntax highlighting and markup formatting, though the formatting depends on whether or not your html is well-formed.

Just add this as a class for your RichTextBox, instantiate it accordingly and call it within whichever event works for you (I'm using it with the RTB's double_click event but that does eliminate double-click text selection). What I'm planning to do is add a timer, some boolean variables and work this within the key_up and key_down events to set the highlight update to be a bit more automatic and less intrusive on shortcuts. (which is hereby included below the class)

    public void HighlightHTM(RichTextBox Htm_Input)
    {
        Htm_Input.Visible = false;
        #region store the original caret position + forecolor
        int originalIndex = Htm_Input.SelectionStart;
        int originalLength = Htm_Input.SelectionLength;
        Color originalColor = Color.FromArgb(200, 200, 200); // Grey
        #endregion
        #region try to format the markup
        try { Htm_Input.Text = XElement.Parse(Htm_Input.Text).ToString(); } catch { }
        #endregion
        #region match everything but puncutation and equals
        Regex e = new Regex(@"(.*?|=)[^\w\s]");
        MatchCollection eMatches = e.Matches(Htm_Input.Text);
        foreach (Match m in eMatches)
        {
            Htm_Input.SelectionStart = m.Groups[1].Index;
            Htm_Input.SelectionLength = m.Groups[1].Length;
            Htm_Input.SelectionColor = Color.FromArgb(221, 202, 126); // Yellow
        }
        #endregion
        #region match tags
        Regex t = new Regex(@"(<\w+|</\w+|/>|>)[^=]");
        MatchCollection tMatches = t.Matches(Htm_Input.Text, 0);
        foreach (Match m in tMatches)
        {
            Htm_Input.SelectionStart = m.Groups[1].Index;
            Htm_Input.SelectionLength = m.Groups[1].Length;
            Htm_Input.SelectionColor = Color.FromArgb(167, 146, 90); // Brown
        }
        #endregion
        #region match quotes
        Regex q = new Regex("\".*?\"");
        MatchCollection qMatches = q.Matches(Htm_Input.Text);
        foreach (Match m in qMatches)
        {
            Htm_Input.SelectionStart = m.Index;
            Htm_Input.SelectionLength = m.Length;
            Htm_Input.SelectionColor = Color.FromArgb(150, 179, 138); // Green
        }
        #endregion
        #region match inner html
        Regex h = new Regex(">(.+?)<");
        MatchCollection hMatches = h.Matches(Htm_Input.Text);
        foreach (Match m in hMatches)
        {
            Htm_Input.SelectionStart = m.Groups[1].Index;
            Htm_Input.SelectionLength = m.Groups[1].Length;
            Htm_Input.SelectionColor = Color.FromArgb(200, 200, 200); // Grey
        }
        #endregion
        #region restoring the original colors, for further writing
        Htm_Input.SelectionStart = originalIndex;
        Htm_Input.SelectionLength = originalLength;
        Htm_Input.SelectionColor = originalColor; // Light Grey
        #endregion
        Htm_Input.Focus();
        Htm_Input.Visible = true;
    }

Happy coding!

Edit: I should also mention that !doctype breaks formatting as it's not exactly xml-friendly in the context of "well-formed". For my purposes, all tags including body and relevant closings, css and js links are added programmatically at page save so only markup within the body tags are worked with inside the html RTB. This eliminates that problem.

You'll notice that this relies exclusively on Regex rather than on hard-coded tags and properties. I did this because tags and properties have a tendency to pop on and off the w3 scene quite often. That would force a dev to continually have to go back and edit those strings to remove deprecated tags / properties or to add new. Not optimal.

I also thought it prudent to go ahead and include the instantiation / usage examples to make this a bit more plug&play.

Above public Main(), instantiate like so:

    #region Class Instantiation
    SyntaxHighlight syntax = new SyntaxHighlight();
    #endregion

... and, within your chosen event handler, call it like so:

    private void htm_input_DoubleClick(object sender, EventArgs e)
    {
        syntax.HighlightHTM(Htm_Input);
    }

Naturally, adding a SaveFileDialog and an OpenFileDialog pretty much provides this the functionality of your very own, albeit very basic, html editor. Toss in a WebBrowser control and apply the RTB's text as the WebBrowser's source and you've upgraded to live-view.

In the very least, this should serve as a viable reference for syntax highlighting in general. It really just boils down to identifying patterns and manipulating their colors so, for example, this will work effectively with css, javascript and even C# with some light adjusting of the pattern identification parameters.

The following is how I setup the automatic refresh with key_up / key_down and a timer set to 1000 ms:

    #region Globals
    int r = 0;
    bool refresh = false;
    #endregion
    private void Htm_Input_KeyUp(object sender, KeyEventArgs e)
    {
        refresh = true; // enter refresh cycle
    }
    private void Htm_Input_KeyDown(object sender, KeyEventArgs e)
    {
        refresh = false; // abort refresh cycle
    }
    private void Timer_Refresh_Tick(object sender, EventArgs e)
    {
        // check if refresh cycle is entered, refresh at 3 seconds or reset the counter if aborted
        if (refresh) { if (r == 3) { syntax.HighlightHTM(Htm_Input); refresh = false; r = 0; } r++; } else { r = 0; }
    }
BJS3D
  • 149
  • 1
  • 11