实现一个智能下拉提示输入的功能,使用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
}
}