自动完成菜单






4.89/5 (238投票s)
为 RichTextBox、TextBox 及其他控件提供可自定义的自动完成菜单
![]() | ![]() |
引言
我们都使用 VisualStudio 的自动完成菜单,也称为 IntelliSense。这个功能非常有用,不是吗?不幸的是,.NET FW 没有内置的自动完成菜单组件。本控件旨在填补这一空白。
AutocompleteMenu
允许您轻松地将下拉提示嵌入到窗体上的任何 TextBox
或 RichTextBox
中。
实现
该组件包含几个类。下面总结了主要类及其功能
AutocompleteMenu
- 主要组件,包含基本功能。它订阅 TextBox
的事件,查找合适的选项,显示下拉菜单并向 textbox
插入新文本。
以下是 AutocompleteMenu
的基本属性
AllowTabKey
- 允许使用 TAB 键选择菜单项AppearInterval
- 菜单出现间隔 (毫秒)ImageList
- 用于菜单项的图像列表Items
- 菜单项文本列表(使用AutocompleteMenu
的最简单方式)MaximumSize
- 弹出菜单的最大尺寸MinFragmentLength
- 菜单出现的最小片段长度。AutocompleteMenu
仅在当前光标周围的片段至少包含MinFragmentLength
个字符时出现SearchPattern
- 用于搜索光标周围片段的正则表达式模式
AutocompleteMenuHost
- 派生自 ToolStripDropDown
的可视化组件。此控件允许您在不失去主窗体焦点的情况下显示菜单。
AutocompleteListView
- 派生自 UserControl
的可视化组件。它使用 GDI+ 绘制下拉菜单的项。此组件类似于 ListView
,但它允许您以良好的性能显示大量元素。
AutocompleteItem
- 菜单项。此类包含有关菜单项的所有必要信息。您可以通过继承 AutocompleteItem
并重写其虚拟方法来扩展菜单功能。以下是 AutocompleteItem
的基本属性
Text
- 要插入textbox
的文本MenuText
- 将显示在弹出菜单中的文本ImageIndex
- 此项的图像索引ToolTipTitle
- 工具提示的标题。如果ToolTipTitle
为null
,则此项不会显示工具提示ToolTipText
- 工具提示的文本Tag
- 您可以在此处存储任何数据
以下是一些您可以重写的方法
GetTextForReplace
- 返回要插入的文本。您可以动态更改要插入的文本。例如,您可以插入当前日期。Compare
- 此方法定义此项是否在菜单中可见。默认情况下,此方法仅使以给定片段开头的项可见。但您可以覆盖此行为。例如,您可以按子字符串比较,或进行模糊比较。OnSelected
- 在文本已插入textbox
时调用此方法。在这里,您可以对文本进行一些额外的操作。例如,您可以将光标移动到某个位置。
该库还包含几个派生自 AutocompleteItem
的有用类:SnippetAutocompleteItem
(可用于插入代码片段)、MethodAutocompleteItem
(可用于在点后插入方法名称)、SubstringAutocompleteItem
(按子字符串比较文本)、MulticolumnAutocompleteItem
(绘制多列菜单)。
Using the Code
简单用法
- 将
AutocompleteMenu
组件放置在您的窗体上。 - 在
AutocompleteMenu.Items
中键入菜单项的文本。 - 设置
TextBox
的AutocompleteMenu
属性。 - 就这样,各位!
高级用法
- 将
AutocompleteMenu
组件放置在您的窗体上。 - 创建一个项目列表,并使用
SetAutocompleteItems()
或AddItem()
方法将其添加到菜单中。例如
string[] snippets = { "if(^)\n{\n}", "if(^)\n{\n}\nelse\n{\n}",
"for(^;;)\n{\n}", "while(^)\n{\n}", "do${\n^}while();", "switch(^)\n{\n\tcase : break;\n}" };
private void BuildAutocompleteMenu()
{
var items = new List<AutocompleteItem>();
foreach (var item in snippets)
items.Add(new SnippetAutocompleteItem(item) { ImageIndex = 1 });
//set as autocomplete source
autocompleteMenu1.SetAutocompleteItems(items);
}
此外,您还可以添加自己的项,这些项派生自 AutocompleteItem
。例如
internal class EmailSnippet : AutocompleteItem
{
public EmailSnippet(string email): base(email)
{
ImageIndex = 0;
ToolTipTitle = "Insert email:";
ToolTipText = email;
}
public override CompareResult Compare(string fragmentText)
{
if (fragmentText == Text)
return CompareResult.VisibleAndSelected;
if (fragmentText.Contains("@"))
return CompareResult.Visible;
return CompareResult.Hidden;
}
}
有关更多详细信息,请参阅演示应用程序的 AdvancedSample
示例。
快捷方式
您可以使用以下快捷键
- Ctrl+Space - 强制打开
AutocompleteMenu
- Up, Down, PgUp, PgDown - 在菜单中导航
- Enter, Tab, DblClick on the item - 将选定的项插入文本(仅当
AllowTabKey
为 true 时,Tab 键才有效)。 - Esc - 关闭菜单
请注意,即使焦点在 textbox
中,这些键也会起作用。
单击某项时,该项的工具提示会显示出来。
自定义 ListView
您可以使用自定义控件来显示 AutocompleteMenu
(例如 ListView
、ListBox
、DataGridView
、TreeView
等)。为此,请创建自己的控件(派生自 Control
)并实现 IAutocompleteListView
接口。有关更多详细信息,请参阅 CustomListViewSample
。
动态上下文菜单
通常需要菜单显示一个动态变化的项集,而不是固定的项集,具体取决于文本。此任务可以解决。
请注意,菜单的 SetAutocompleteItems()
方法接受 IEnumerable
作为要显示的项的集合。
因此,您不是在程序开始时生成一个项列表,而是在菜单调用枚举器时动态生成它。
以下代码演示了这一概念
autocompleteMenu1.SetAutocompleteItems(new DynamicCollection(tb));
....
internal class DynamicCollection : IEnumerable<AutocompleteItem>
{
public IEnumerator<AutocompleteItem> GetEnumerator()
{
return BuildList().GetEnumerator();
}
private IEnumerable<AutocompleteItem> BuildList()
{
//find all words of the text
var words = new Dictionary<string, string>();
foreach (Match m in Regex.Matches(tb.Text, @"\b\w+\b"))
words[m.Value] = m.Value;
//return autocomplete items
foreach(var word in words.Keys)
yield return new AutocompleteItem(word);
}
}
有关完整实现的示例,请参阅 DynamicMenuSample
。
兼容性
Autocomplete
菜单与 TextBox
、RichTextBox
、MaskedTextBox
、FastColoredTextBox[^] 和其他派生自 TextBoxBase
的控件兼容。
此外,Autocomplete
Menu 还兼容支持以下属性和方法的任何控件
string SelectedText{get;set;}
int SelectionLength{get;set;}
int SelectionStart{get;set;}
Point GetPositionFromCharIndex(int charPos)
即使您的控件不支持这些方法,您也可以创建自己的包装器。为此,您需要创建自己的包装器类并在此处实现 ITextBoxWrapper
接口。
以下描述了 ITextBoxWrapper
的方法和属性
public interface ITextBoxWrapper
{
Control TargetControl { get; }
string Text { get; }
string SelectedText { get; set; }
int SelectionLength { get; set; }
int SelectionStart { get; set; }
Point GetPositionFromCharIndex(int pos);
event EventHandler LostFocus;
event ScrollEventHandler Scroll;
event KeyEventHandler KeyDown;
event MouseEventHandler MouseDown;
}
因此,在创建包装器后,您可以简单地将 AutocompleteMenu
附加到您的控件。大致如下
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//attach myControl1 to autocompleteMenu1
autocompleteMenu1.TargetControlWrapper = new MyControlWrapper(myControl1);
}
}
internal class MyControlWrapper : ITextBoxWrapper
{
private MyControl tb;
public MyControlWrapper(MyControl tb)
{
this.tb = tb;
}
//here we implement ITextBoxWrapper
.....
}
示例
演示应用程序包含几个示例
SimplestSample
- 展示了如何以最简单的方式使用该控件。CustomItemSample
- 展示了如何创建派生自AutocompleteItem
的自定义类。AdvancedSample
- 展示了如何创建具有关键字、代码片段、方法建议、文本校正器等的自定义自动完成菜单。ExtraLargeSample
- 演示了该组件处理海量菜单项(一百万个)的性能。ComboboxSample
- 展示了如何创建类似Combobox
的控件,但具有非常大的下拉列表并支持按子字符串搜索。MulticolumnSample
- 展示了如何创建多列自动完成菜单。CustomListViewSample
- 展示了如何在自动完成菜单中托管自定义ListView
。DynamicMenuSample
- 此示例展示了如何创建动态的、上下文敏感的自动完成菜单。DataGridViewSample
- 展示了如何将AutocompleteMenu
附加到DataGridView
。
历史
- 2012 年 4 月 13 日 - 首次发布
- 2012 年 4 月 21 日 - 对控件进行了重构。增加了对
FastColoredTextBox
[^] 和其他控件的支持。 - 2012 年 5 月 9 日 - 对控件进行了重构。添加了一些示例。
- 2014 年 12 月 3 日 - 添加了 Colors 属性。
- 2015 年 2 月 8 日 - 修复了一些小错误。
- 2015 年 3 月 19 日 - 修复了一些小错误。