Click here to Skip to main content
15,180,720 members
Articles / Desktop Programming / Windows Forms
Technical Blog
Posted 2 Apr 2010

Stats

77K views
26 bookmarked

XML Editor Control

Rate me:
Please Sign up or sign in to vote.
4.47/5 (11 votes)
2 Apr 2010CPOL2 min read
XML Editor with syntax highlighting

If you have a Windows Forms application that involves XML editing or viewing, you can use this control to save yourself the effort of formatting the XML content. For now, only syntax highlighting is implemented. I expect to add more features in the future like spacing, grouping, intellisense, etc…

Usage

Simply add the files (XmlToken.cs, XmlTokenizer.cs, XmlEditor.cs, XmlEditor.designer.cs) to your project, then drag and drop the XmlEditor control from the Toolbox into your Windows Form.

The XmlEditor control currently has three public properties. Use AllowXmlFormatting to enable or disable formatting on the XML content in the editor. The ReadOnly property tells whether or not to allow the user to change the text. The Text property sets or gets the text of the XMLeditor.

Here is how the control looks like when AllowXmlFormatting = true and ReadOnly = false (default values):

Implementation

To color the XML string, we have to split it into multiple tokens, then color each token based on its type. I have identified the following token types (based on syntax highlighting behavior in Visual Studio 2008):

  • A “Value” is anything between double quotes
  • A “Comment” is anything that starts with <!– and ends with –> (or starts with <!– and is never closed with –>)
  • An “Element” is any letter or digit that falls between < and a space or >
  • An “Attribute” is any letter or digit that falls after a < followed by space and not closed by >
  • An “Escape” is anything that starts with & and ends with ; (For example &quote;)
  • A “SpecialChar” is any character that is not a letter or a digit
  • A “None” is anything else

The Tokenize() public static method of the XmlTokenizer class does the job of splitting a string into XML tokens.

An XmlToken object is a representation of an XML token with details about the exact text of that token, its location in the string and its type.

Here is the code in the XmlEditor control that does the syntax highlighting:

C#
List<XmlToken> tokens = XmlTokenizer.Tokenize(xmlEditor.Text);

foreach (XmlToken token in tokens)
{
    xmlEditor.Select(token.Index, token.Text.Length);

    switch (token.Type)
    {
        case XmlTokenType.Attribute:
            xmlEditor.SelectionColor = Color.Red;
            break;
        case XmlTokenType.Comment:
            xmlEditor.SelectionColor = Color.DarkGreen;
            break;

        //  and so on for the other token types
    }
}

You can look at the code on my GitHub page.


Filed under: csharp, WinForms, XML

This article was originally posted at http://mycodelog.com/2010/04/01/xml-editor

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ali BaderEddin
Software Developer Qualtrics
United States United States
https://about.me/ali.b

Comments and Discussions

 
GeneralWorks great for viewing, needed some tweaks for editing Pin
Fr33dan6-Apr-20 6:18
MemberFr33dan6-Apr-20 6:18 
This code works great if you want to display some XML, but by taking the time to tokenize the XML with each update means typing into the editor is dog slow. I took the code and modified it for a solution to this problem.

Instead of formatting on each TextChanged event I start/restart a timer on that event and then do the formatting after that timer fires. This way you can type at a normal pace in the editor, as a trade off you must wait a moment for the formatting to apply but I think this makes the control much more usable.

I also added some code to the tokenizing to make sure that the scroll location is preserved and a couple other minor quality of life updates. XmlEditor.cs looks like this now:

C#
///---------------------------------------------------------------------------
/// File Name:      XmlEditor.cs
/// Description:    Editor that does Xml formatting and syntax highlighting.
/// 
/// Author:         Ali Badereddin, Joseph Tignor
/// Created:        26/12/2009 - Ali Badereddin
/// Modified:       06/04/2020 - Joseph Tignor
///---------------------------------------------------------------------------

#region Using Directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

#endregion

/// <summary>
/// Editor that does Xml formatting and syntax highlighting.
/// </summary>
public partial class XmlEditor : UserControl
{
    #region Instance Variables

    private static Color specialCharColor = Color.Blue;   //  Color for special characters
    private static Color escapeColor = Color.Orchid;      //  Color for escape sequences
    private static Color elementColor = Color.DarkRed;    //  Color for Xml elements
    private static Color attributeColor = Color.Red;      //  Color for Xml attributes
    private static Color valueColor = Color.DarkBlue;     //  Color for attribute values
    private static Color commentColor = Color.DarkGreen;  //  Color for Xml comments

    private bool allowXmlFormatting = true;               //  Whether to do Xml formatting when text changes

    private bool tokenizing;                              //  If the control is currently in the process of tokenizing the contents.

    #endregion

    #region Constructor

    /// <summary>
    /// Constructor
    /// </summary>
    public XmlEditor()
    {
        InitializeComponent();
    }

    #endregion

    #region Properties

    /// <summary>
    /// Set or get the text of the xml editor.
    /// </summary>
    public override string Text
    {
        set
        {
            xmlTextBox.Text = value;
        }
        get
        {
            return xmlTextBox.Text;
        }
    }

    /// <summary>
    /// Tells whether to format the editor's Xml or not.
    /// </summary>
    [Category("Behavior"),Description("Allow XML formatting of the editor contents.")]
    public bool AllowXmlFormatting
    {
        set
        {
            allowXmlFormatting = value;
        }
        get
        {
            return allowXmlFormatting;
        }
    }

    /// <summary>
    /// Whether to allow the user to change text.
    /// </summary>
    [Category("Behavior"), Description("Whether to allow the user to change text.")]
    public bool ReadOnly
    {
        set
        {
            this.xmlTextBox.ReadOnly = value;
        }
        get
        {
            return this.xmlTextBox.ReadOnly;
        }
    }

    #endregion

    #region UI Events
    
    /// <summary>
    /// Start the timer to format new XML changes.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void xmlTextBox_TextChanged(object sender, EventArgs e)
    {
        if (allowXmlFormatting && !tokenizing)
        {
            this.tokenizerTimer.Stop();
            this.tokenizerTimer.Start();
        }
    }

    /// <summary>
    /// Format new XML changes.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void tokenizerTimer_Tick(object sender, EventArgs e)
    {
        tokenizerTimer.Stop();

        tokenizing = true;
        FormatXml(this.xmlTextBox);
        tokenizing = false;
    }

    #endregion

    #region Methods

    /// <summary>
    /// Format Xml in the passed rich text box.
    /// </summary>
    /// <param name="xmlEditor"></param>
    public static void FormatXml(RichTextBox xmlEditor)
    {
        //  Stop redrawing
        RichTextDrawing.StopRedraw(xmlEditor);

        // Get first and last displayed character
        int start = xmlEditor.GetCharIndexFromPosition(new Point(0, 0));
        int end = xmlEditor.GetCharIndexFromPosition(new Point(xmlEditor.ClientSize.Width, xmlEditor.ClientSize.Height));

        // Save cursor position
        int cursor_position = xmlEditor.SelectionStart;
        int cursor_lenght = xmlEditor.SelectionLength;

        //  Tokenize the Xml string
        List<XmlToken> tokens = XmlTokenizer.Tokenize(xmlEditor.Text);
        foreach (XmlToken token in tokens)
        {
            xmlEditor.Select(token.Index, token.Text.Length);
            switch (token.Type)
            {
                case XmlTokenType.None:
                    xmlEditor.SelectionColor = xmlEditor.ForeColor;
                    break;
                case XmlTokenType.SpecialChar:
                    xmlEditor.SelectionColor = specialCharColor;
                    break;
                case XmlTokenType.Escape:
                    xmlEditor.SelectionColor = escapeColor;
                    break;
                case XmlTokenType.Element:
                    xmlEditor.SelectionColor = elementColor;
                    break;
                case XmlTokenType.Attribute:
                    xmlEditor.SelectionColor = attributeColor;
                    break;
                case XmlTokenType.Value:
                    xmlEditor.SelectionColor = valueColor;
                    break;
                case XmlTokenType.Comment:
                    xmlEditor.SelectionColor = commentColor;
                    break;
            }
        }

        // Scroll to the last character and then to the first + line width
        xmlEditor.SelectionLength = 0;
        xmlEditor.SelectionStart = end;
        xmlEditor.ScrollToCaret();
        xmlEditor.SelectionStart = start + xmlEditor.Lines[xmlEditor.GetLineFromCharIndex(start)].Length + 1;
        xmlEditor.ScrollToCaret();

        // Finally, set cursor to original position
        xmlEditor.SelectionStart = cursor_position;
        
        //  Resume redraw
        RichTextDrawing.RestoreRedraw(xmlEditor);
    }

    #endregion

}

#region Helper Class

/// <summary>
/// Helper class to change colors on a RichTextBox without flickering.
/// </summary>
public class RichTextDrawing
{
    private static int lastSelection;

    [DllImport("user32.dll")]
    public static extern bool LockWindowUpdate(IntPtr hWndLock);

    public static void StopRedraw(RichTextBox richTextBox)
    {
        LockWindowUpdate(richTextBox.Handle);

        //  Save the last location 
        lastSelection = richTextBox.SelectionStart;

        // Refresh colors
        richTextBox.SelectAll();
        richTextBox.SelectionColor = richTextBox.ForeColor;
    }

    public static void RestoreRedraw(RichTextBox richTextBox)
    {
        LockWindowUpdate(IntPtr.Zero);

        //  Restore selection and color state
        richTextBox.SelectionStart = lastSelection;
        richTextBox.SelectionLength = 0;
        richTextBox.SelectionColor = richTextBox.ForeColor;
    }
}

#endregion

QuestionThanks Pin
Member 1025158131-Aug-17 2:16
MemberMember 1025158131-Aug-17 2:16 
QuestionPerformance Pin
Member 1139632521-Dec-16 22:26
MemberMember 1139632521-Dec-16 22:26 
GeneralThanks Pin
Ritesh Man Chtirakar23-Sep-15 20:03
MemberRitesh Man Chtirakar23-Sep-15 20:03 
GeneralRe: Thanks Pin
Ali BaderEddin8-Oct-15 9:11
MemberAli BaderEddin8-Oct-15 9:11 
GeneralMy vote of 1 Pin
Grumpyman31-Jan-15 7:21
MemberGrumpyman31-Jan-15 7:21 
GeneralRe: My vote of 1 Pin
Ali BaderEddin31-Jan-15 14:40
MemberAli BaderEddin31-Jan-15 14:40 
QuestionAdd this to auto complete tags Pin
poteb7-Aug-12 4:21
Memberpoteb7-Aug-12 4:21 
AnswerRe: Add this to auto complete tags Pin
Ali BaderEddin7-Aug-12 8:12
MemberAli BaderEddin7-Aug-12 8:12 
QuestionThanks for sharing Pin
hbehar5-Jul-12 2:21
Memberhbehar5-Jul-12 2:21 
QuestionPass text to control for formatting Pin
ijourneaux25-Dec-11 6:48
Memberijourneaux25-Dec-11 6:48 
QuestionHighlighting is very slow Pin
mubed1-Dec-11 2:28
Membermubed1-Dec-11 2:28 
AnswerRe: Highlighting is very slow Pin
Ali BaderEddin2-Dec-11 20:41
MemberAli BaderEddin2-Dec-11 20:41 
GeneralMy vote of 5 Pin
AndyTanYuLin28-Nov-11 21:16
MemberAndyTanYuLin28-Nov-11 21:16 
QuestionGreat tool - When is next update Pin
zzfive00320-Sep-11 8:48
Memberzzfive00320-Sep-11 8:48 
AnswerRe: Great tool - When is next update Pin
Ali BaderEddin24-Sep-11 19:14
MemberAli BaderEddin24-Sep-11 19:14 
QuestionNice Pin
shelby6716-Aug-11 19:22
Membershelby6716-Aug-11 19:22 
AnswerRe: Nice Pin
Ali BaderEddin20-Aug-11 22:50
MemberAli BaderEddin20-Aug-11 22:50 
GeneralCrashed with lots of characters Pin
zhanghaocol25-Oct-10 23:06
Memberzhanghaocol25-Oct-10 23:06 
GeneralRe: Crashed with lots of characters Pin
Ali BaderEddin26-Oct-10 12:03
MemberAli BaderEddin26-Oct-10 12:03 
GeneralUndo is completely broken Pin
nikdownload25-Sep-10 7:44
Membernikdownload25-Sep-10 7:44 
GeneralRe: Undo is completely broken Pin
Ali BaderEddin24-Oct-10 11:17
MemberAli BaderEddin24-Oct-10 11:17 
GeneralWeb based XML Editor Control Pin
UdayanDas22-Jul-10 1:00
MemberUdayanDas22-Jul-10 1:00 
GeneralGreat control Pin
ruben ruvalcaba6-Apr-10 5:16
Memberruben ruvalcaba6-Apr-10 5:16 
GeneralRe: Great control Pin
Ali BaderEddin6-Apr-10 8:23
MemberAli BaderEddin6-Apr-10 8:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.