C#实现智能提示输入,并增色显示

实现一个智能下拉提示输入的功能,使用RichTextBox实现,结合了ListBox 实现特定字符提示输入选项 并对关键选项标记颜色突出显示

效果如下:

代码如下:

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SmartBox
{
    public class SmartRichTextBox : RichTextBox
    {
        private ListBox _suggestionList;
        private List<string> _variableList;
        private string _triggerChar = "#";
        private Form _parentForm;

        // 语法高亮颜色
        private Color _variableColor = Color.Blue;
        private Color _normalColor = Color.Black;

        // 用于防止重入的标记
        private bool _isHighlighting = false;

        public SmartRichTextBox()
        {
            _variableList = new List<string>
        {
            "username", "email", "phone", "address",
            "date", "time", "user_id", "department"
        };

            InitializeSuggestionList();
            this.KeyDown += SmartRichTextBox_KeyDown;
            this.KeyUp += SmartRichTextBox_KeyUp;
            this.TextChanged += SmartRichTextBox_TextChanged;
            this.LostFocus += SmartRichTextBox_LostFocus;
            this.SelectionChanged += SmartRichTextBox_SelectionChanged;

            // 设置默认字体和颜色
            this.Font = new Font("Microsoft Sans Serif", 9f);
            this.ForeColor = _normalColor;
        }

        public List<string> VariableList
        {
            get { return _variableList; }
            set { _variableList = value; }
        }

        public string TriggerChar
        {
            get { return _triggerChar; }
            set { _triggerChar = value; }
        }

        public Color VariableColor
        {
            get { return _variableColor; }
            set { _variableColor = value; }
        }

        private void InitializeSuggestionList()
        {
            _suggestionList = new ListBox
            {
                Visible = false,
                Width = this.Width,
                Height = 120,
                Font = this.Font,
                BorderStyle = BorderStyle.FixedSingle
            };

            _suggestionList.Click += SuggestionList_Click;
            _suggestionList.KeyDown += SuggestionList_KeyDown;
            _suggestionList.MouseMove += SuggestionList_MouseMove;
        }

        protected override void OnParentChanged(EventArgs e)
        {
            base.OnParentChanged(e);
            if (this.Parent != null)
            {
                _parentForm = this.FindForm();
                if (_parentForm != null)
                {
                    _parentForm.Controls.Add(_suggestionList);
                    _suggestionList.BringToFront();
                }
            }
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (_suggestionList != null)
            {
                _suggestionList.Width = this.Width;
            }
        }

        #region 事件处理
        private void SmartRichTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.ControlKey)
                return;

            ShowSuggestions();
        }

        private void SmartRichTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (!_suggestionList.Visible)
                return;

            switch (e.KeyCode)
            {
                case Keys.Down:
                    if (_suggestionList.Items.Count > 0)
                    {
                        _suggestionList.SelectedIndex = 0;
                        _suggestionList.Focus();
                    }
                    e.Handled = true;
                    break;
                case Keys.Enter:
                    ApplySuggestion();
                    e.Handled = true;
                    break;
                case Keys.Escape:
                    HideSuggestions();
                    e.Handled = true;
                    break;
            }
        }

        private void SuggestionList_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Enter:
                    ApplySuggestion();
                    e.Handled = true;
                    break;
                case Keys.Escape:
                    HideSuggestions();
                    this.Focus();
                    e.Handled = true;
                    break;
                case Keys.Up:
                    if (_suggestionList.SelectedIndex == 0)
                    {
                        this.Focus();
                        e.Handled = true;
                    }
                    break;
            }
        }

        private void SuggestionList_Click(object sender, EventArgs e)
        {
            ApplySuggestion();
        }

        private void SuggestionList_MouseMove(object sender, MouseEventArgs e)
        {
            // 鼠标悬停时高亮项
            int index = _suggestionList.IndexFromPoint(e.Location);
            if (index >= 0)
            {
                _suggestionList.SelectedIndex = index;
            }
        }

        private void SmartRichTextBox_SelectionChanged(object sender, EventArgs e)
        {
            // 选择改变时隐藏建议列表
            if (_suggestionList.Visible)
            {
                HideSuggestions();
            }
        }

        private void SmartRichTextBox_LostFocus(object sender, EventArgs e)
        {
            // 延迟隐藏,以便点击下拉列表
            BeginInvoke(new Action(() =>
            {
                if (!_suggestionList.Focused && !this.Focused)
                {
                    HideSuggestions();
                }
            }));
        }
        #endregion

        #region 语法高亮功能
        private void SmartRichTextBox_TextChanged(object sender, EventArgs e)
        {
            if (!_isHighlighting)
            {
                ApplySyntaxHighlighting();
            }
        }

        /// <summary>
        /// 应用语法高亮
        /// </summary>
        private void ApplySyntaxHighlighting()
        {
            _isHighlighting = true;

            try
            {
                // 保存当前选择位置和颜色
                int currentSelectionStart = this.SelectionStart;
                int currentSelectionLength = this.SelectionLength;
                Color currentSelectionColor = this.SelectionColor;

                // 首先重置所有文本为默认颜色
                this.SelectAll();
                this.SelectionColor = _normalColor;
                this.DeselectAll();

                // 高亮所有以#开头的变量
                HighlightVariables();

                // 恢复选择位置
                this.SelectionStart = currentSelectionStart;
                this.SelectionLength = currentSelectionLength;
                this.SelectionColor = currentSelectionColor;
            }
            finally
            {
                _isHighlighting = false;
            }
        }

        /// <summary>
        /// 高亮变量
        /// </summary>
        private void HighlightVariables()
        {
            string text = this.Text;

            // 使用正则表达式匹配以#开头的变量
            // 匹配模式:以#开头,后面跟着字母、数字、下划线,直到遇到空格或标点
            string pattern = @"#\w+";
            var matches = Regex.Matches(text, pattern);

            foreach (Match match in matches)
            {
                // 检查匹配的变量是否在变量列表中(去掉#号)
                string variableName = match.Value.Substring(1); // 去掉#号
                if (_variableList.Contains(variableName, StringComparer.OrdinalIgnoreCase))
                {
                    this.Select(match.Index, match.Length);
                    this.SelectionColor = _variableColor;
                }
            }
        }

        /// <summary>
        /// 手动刷新高亮(在变量列表改变后调用)
        /// </summary>
        public void RefreshHighlighting()
        {
            ApplySyntaxHighlighting();
        }
        #endregion

        #region 下拉建议功能
        private void ShowSuggestions()
        {
            string text = this.Text;
            int cursorPos = this.SelectionStart;

            // 查找触发字符的位置
            int triggerIndex = text.LastIndexOf(_triggerChar, cursorPos - 1);
            if (triggerIndex >= 0)
            {
                // 检查#号后面是否已经有空格或其他分隔符
                bool hasSpaceAfterTrigger = false;
                for (int i = triggerIndex + 1; i < cursorPos; i++)
                {
                    if (char.IsWhiteSpace(text[i]) || text[i] == '.' || text[i] == ',' || text[i] == ';')
                    {
                        hasSpaceAfterTrigger = true;
                        break;
                    }
                }

                if (!hasSpaceAfterTrigger)
                {
                    string searchText = text.Substring(triggerIndex + 1, cursorPos - triggerIndex - 1);

                    var matches = _variableList
                        .Where(v => v.StartsWith(searchText, StringComparison.OrdinalIgnoreCase))
                        .ToList();

                    if (matches.Count > 0)
                    {
                        _suggestionList.Items.Clear();
                        foreach (var match in matches)
                        {
                            _suggestionList.Items.Add(match);
                        }

                        // 定位下拉列表
                        Point location = this.Location;
                        Control parent = this.Parent;
                        while (parent != null && !(parent is Form))
                        {
                            location.Offset(parent.Location.X, parent.Location.Y);
                            parent = parent.Parent;
                        }

                        _suggestionList.Location = new Point(location.X, location.Y + this.Height);
                        _suggestionList.Visible = true;
                        _suggestionList.BringToFront();

                        if (_suggestionList.Items.Count > 0)
                        {
                            _suggestionList.SelectedIndex = 0;
                        }
                        return;
                    }
                }
            }

            HideSuggestions();
        }

        private void HideSuggestions()
        {
            _suggestionList.Visible = false;
        }

        private void ApplySuggestion()
        {
            if (_suggestionList.SelectedItem != null)
            {
                string selectedVar = _suggestionList.SelectedItem.ToString();
                string text = this.Text;
                int cursorPos = this.SelectionStart;

                // 找到最后一个触发字符的位置
                int triggerIndex = text.LastIndexOf(_triggerChar, cursorPos - 1);
                if (triggerIndex >= 0)
                {
                    // 替换从触发字符到当前位置的文本
                    string newText = text.Substring(0, triggerIndex) +
                                   _triggerChar + selectedVar +
                                   text.Substring(cursorPos);

                    this.Text = newText;
                    this.SelectionStart = triggerIndex + _triggerChar.Length + selectedVar.Length;

                    // 应用语法高亮
                    ApplySyntaxHighlighting();
                }

                HideSuggestions();
                this.Focus();
            }
        }
        #endregion

        #region 公共方法
        /// <summary>
        /// 添加变量到列表
        /// </summary>
        public void AddVariable(string variableName)
        {
            if (!_variableList.Contains(variableName, StringComparer.OrdinalIgnoreCase))
            {
                _variableList.Add(variableName);
                RefreshHighlighting();
            }
        }

        /// <summary>
        /// 从列表中移除变量
        /// </summary>
        public void RemoveVariable(string variableName)
        {
            _variableList.RemoveAll(v => v.Equals(variableName, StringComparison.OrdinalIgnoreCase));
            RefreshHighlighting();
        }

        /// <summary>
        /// 清空所有变量
        /// </summary>
        public void ClearVariables()
        {
            _variableList.Clear();
            RefreshHighlighting();
        }

        /// <summary>
        /// 获取所有高亮的变量(不包含#号)
        /// </summary>
        public List<string> GetHighlightedVariables()
        {
            var variables = new List<string>();
            string text = this.Text;

            string pattern = @"#\w+";
            var matches = Regex.Matches(text, pattern);

            foreach (Match match in matches)
            {
                string variableName = match.Value.Substring(1);
                //判断不区分大小写
                if (_variableList.Contains(variableName, StringComparer.OrdinalIgnoreCase))
                {
                    variables.Add(variableName);
                }
            }

            return variables.Distinct().ToList();
        }
        #endregion

        #region 重写方法
        protected override void OnTextChanged(EventArgs e)
        {
            // 确保基类处理完成后再进行高亮
            base.OnTextChanged(e);
        }

        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            if (_suggestionList != null)
            {
                _suggestionList.Font = this.Font;
            }
        }
        #endregion
    }
}
相关推荐
song8546011343 小时前
uniapp如何集成第三方库
开发语言·uni-app
梵得儿SHI3 小时前
Java IO 流深度解析:对象流与序列化机制(ObjectInputStream/ObjectOutputStream)
java·开发语言·rpc·序列化·对象流·对象与字节流的转换·java对象流
想不明白的过度思考者3 小时前
Rust——Tokio的多线程调度器架构:深入异步运行时的核心机制
开发语言·架构·rust·多线程
百炼成神 LV@菜哥3 小时前
记类成员变量 vs 方法中的变量
java·开发语言
贝西奇谈3 小时前
JavaScript DOM节点操作详解
开发语言·javascript·php
学习编程的Kitty3 小时前
JavaEE初阶——多线程(4)线程安全
java·开发语言·jvm
刚子编程3 小时前
ASP.NET Core Blazor 核心功能一:Blazor依赖注入与状态管理指南
开发语言·.netcore·blazor
海木漄4 小时前
C# 内存是绝对自动清理吗?
开发语言·c#
野老杂谈4 小时前
如何快速学习智能合约开发语言 Solidity
开发语言·学习·智能合约·solidity·以太坊·区块链开发