[整理]如何做一个语法着色控件

前言

很多IDE或者开发工具中都有语法着色的功能,这是如何实现的呢?笔者试着用C#做了一个Sample,基本上实现此功能。
本文一半是原创,一半是参考国外的论坛

思路

语法着色器,实际上只做了两件事情:“接受用户输入”以及“改变关键字字体属性”。

1)首先看第一个:接受用户输入。

C#自带的控件中能够满足需求的首推“RichTextBox”,笔者在网上经常看到很多同行都以此为基础,制作出类似MS-Word的种种效果。既然,复杂的Word效果都能够实现,那么语法着色自然也就不在话下。

2)其次,最重要的“改变关键字字体属性”。

这一点还可分成三个更小的工作:关键字列表、判断关键字、定义字体属性。
  • 关键字列表很简单,一个List即可满足需求。
    这里以基本SQL语句为例。
    private IList<String> _KeyWords = new List<String>()
    
    {
    
        "SELECT", "UPDATE", "DELETE", "INSERT", "DROP", "FROM", "WHERE", "ALERT", "CREATE", "TABLE",
    
        "VALUES", "NULL",
    
        "AND", "OR", "NOT", "IN", "INTO", "UNION", "AS", "IS",
    
        "COUNT", "MAX", "MIN", "AVG",
    
        "WAIT", "NOWAIT", "WITHOUT", "LOCK", "GROUP", "ORDER", "BY",
    
        "JOIN", "LEFT", "RIGHT", "CASE", "WHEN", "ELSE", "END",
    
        "COMMIT", "ROLLBACK"
    
    };
  • 判断关键字
    为了在用户输入时能够立即对所输入的内容有所反映,我们需要override一下RichTextBox的OnTextChanged()方法。
    其他属性及方法介绍请参考MSDN
    笔者的算法很简单,遍历业已定义好的关键字列表,用其与用户输入的内容进行比较,如果发现相同,则按照字符的index值计算编辑区域。
  • 定义字体属性
    在C#中有一个叫做FONT的类能够满足需求。 其他属性及方法介绍请参考MSDN

    注意事项

    笔者在尝试的过程中发现屏幕中显示的内容会随着用户的输入,出现闪烁。究其原因可能是因为Windows一直在重新绘制窗口造成的,为了回避这个问题,我们可以使用SuspendLayout()和ResumeLayout()暂停和恢复画面重新绘制的过程。

    做完这几步,我们的语法着色控件也就大功告成了。下面是全部源代码。在VS2008 Express版本上调试通过。
    using System;
    
    using System.Collections.Generic;
    
    using System.Linq;
    
    using System.Text;
    
    using System.Windows.Forms;
    
    using System.Text.RegularExpressions;
    
    using System.Drawing;
    
    using System.ComponentModel;
    
    
    
    namespace Base
    
    {
    
        public partial class SQLEditor : RichTextBox
    
        {
    
            
    
            /// <summary>
    
            /// 关键字列表
    
            /// </summary>
    
            private IList<String> _KeyWords = new List<String>()
    
            {
    
                "SELECT", "UPDATE", "DELETE", "INSERT", "DROP", "FROM", "WHERE", "ALERT", "CREATE", "TABLE",
    
                "VALUES", "NULL",
    
                "AND", "OR", "NOT", "IN", "INTO", "UNION", "AS", "IS",
    
                "COUNT", "MAX", "MIN", "AVG",
    
                "WAIT", "NOWAIT", "WITHOUT", "LOCK", "GROUP", "ORDER", "BY",
    
                "JOIN", "LEFT", "RIGHT", "CASE", "WHEN", "ELSE", "END",
    
                "COMMIT", "ROLLBACK"
    
            };
    
            [CategoryAttribute("KeyWords"), Description("关键字列表")]
    
            public IList<String> KeyWords
    
            {
    
                get
    
                {
    
                    return this._KeyWords;
    
                }
    
                set
    
                {
    
                    this._KeyWords = value;
    
                }
    
            }
    
    
    
            /// <summary>
    
            /// 关键字颜色(默认为蓝色)
    
            /// </summary>
    
            private Color _KeyWordsColor = Color.Blue;
    
            [CategoryAttribute("KeyWordsColor"), Description("关键字颜色")]
    
            public Color KeyWordsColor
    
            {
    
                get
    
                {
    
                    return this._KeyWordsColor;
    
                }
    
                set
    
                {
    
                    this._KeyWordsColor = value;
    
                }
    
            }
    
    
    
            // TODO 数字,字符串属性
    
    
    
            /// <summary>
    
            /// 分隔符
    
            /// </summary>
    
            Regex tokens = new Regex(@"[\s\r\n\t\(\)\;]");
    
    
    
            /// <summary>
    
            /// 接受输入时语法着色
    
            /// </summary>
    
            /// <param name="e"></param>
    
            protected override void OnTextChanged(EventArgs e)
    
            {
    
                base.OnTextChanged(e);
    
    
    
                // TODO DEBUG
    
                Console.WriteLine("POINT:" + this.SelectionStart + "|" + "SIZE:" + this.Text.Length);
    
    
    
                // 暂停屏幕刷新,防止闪烁
    
                SuspendLayout();
    
    
    
                // 缓存输入焦点位置
    
                int inputPoint = this.SelectionStart;
    
    
    
                String[] wrk_words = tokens.Split(this.Text.ToUpper());
    
    
    
                // 判断是否有必要进行着色
    
                if (this.Text.Length == 0) return;
    
    
    
                // 克隆关键字列表
    
                IList<String> C_KeyWords = new List<String>(this._KeyWords);
    
                // 关键字开始坐标
    
                int iStart = 0;
    
    
    
                // 初始化
    
                this.SelectionStart = 0;
    
                this.SelectionLength = this.Text.Length;
    
                this.SelectionColor = Color.Black;
    
                this.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
    
    
    
                // 逐字匹配
    
                foreach (String word in wrk_words)
    
                {
    
                    foreach (String keyw in C_KeyWords)
    
                    {
    
                        // 判断当前值是否为关键字
    
                        if (word.Equals(keyw))
    
                        {
    
                            // 查找坐标
    
                            iStart = this.Text.ToUpper().IndexOf(keyw);
    
    
    
                            // 判断是否存在处理对象
    
                            while (iStart >= 0)
    
                            {
    
                                this.SelectionStart = iStart;
    
                                this.SelectionLength = keyw.Length;
    
    
    
                                this.SelectionColor = Color.Blue;
    
                                this.SelectionFont = new Font("Courier New", 10, FontStyle.Bold);
    
    
    
                                // 获取下一次处理开始坐标
    
                                iStart = this.Text.ToUpper().IndexOf(keyw, this.SelectionStart + 1);
    
    
    
                                this.SelectionStart = this.Text.Length;
    
                                this.SelectionLength = 0;
    
                            }
    
                        }
    
                        // break;
    
    
    
                    }
    
    
    
                }
    
    
    
                // 计算输入点的位置,如果不在末尾,则说明用户在修改前面的内容,否则将焦点移到末尾
    
                this.SelectionStart = (inputPoint < this.Text.Length) ? inputPoint : this.Text.Length;
    
                this.SelectionLength = 0;
    
    
    
                ResumeLayout();
    
            }
    
        }
    
    }
    
    
  • 你可能感兴趣的:(语法)