Conmajia

Stop stealing sheep!

导航

< 20253 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

AutoComplete菜单控件 for .NET

自动完成菜单

[乌克兰]Pavel TorgashovConmajia 译

自定义用于RichTextBoxTextBox和其他控件的自动完成菜单


 codeproject.com2012四月最佳C#文章获奖作品


阅读原文

 下载源代码 - 192.4KB

 下载DEMO - 22.9KB

 

    

简介

我们所有人都用过VisualStudio的自动完成菜单也就是IntelliSense它非常管用不是吗不幸的是.NET框架并没有包含内置的自动完成菜单组件本文制作的组件将填补这个空缺

AutocompleteMenu允许你轻松地在你的窗体上任何 TextBox或是RichTextBox里加入下拉提示框功能就像上面图中演示的那样——野比注

实现

该组件包含了数个类下面是主要的类极其功能小结

AutocompleteMenu - 包含基本功能的主要组件它订阅TextBox的事件查找合适的变体显示一个下拉菜单并将新的文字插入文本框

下面是AutocompleteMenu的基本属性

  • AllowTabKey - 允许使用TAB键选择菜单项
  • AppearInterval - 菜单显示的间隔毫秒
  • ImageList - 保存菜单项用到的图片
  • Items - 菜单项列表AutocompleteMenu最简单的用法
  • MaximumSize - 弹出菜单最大尺寸
  • MinFragmentLength - 菜单显示的最小片段长度只有当光标处当前片段长度不低于MinFragmentLength才会显示AutocompleteMenu
  • SearchPattern - 搜索光标处片段的正则表达式

AutocompleteMenuHost - 从ToolStripDropDown派生的可视化组件该控件能让你在不丢失窗体焦点的情况下显示菜单

AutocompleteListView - 从UserControl继承的可视化组件使用GDI+绘制下拉菜单的菜单项该控件和ListView很像但能够高效地显示大量的元素

AutocompleteItem - 菜单项这个类包含了菜单项的所有必要信息你可以从AutocompleteItem继承出你的元素并覆盖其虚方法这样来扩展菜单功能下面是AutocompleteItem的基本属性

  • Text - 要插入文本框的文本
  • MenuText - 显示在弹出菜单上的文本
  • ImageIndex - 菜单项的图片索引
  • ToolTipTitle - 工具提示标题如果ToolTipTitlenull则不会显示工具提示
  • ToolTipText - 工具提示文本
  • Tag - 你可在此附加任何数据

下面是一些你可以重写的方法

  • GetTextForReplace - 返回要插入的文本你可以动态修改要插入的文本例如你可以插入当前日期
  • Compare - 这个方法定义了菜单项显示与否默认情况下只有菜单项以给定的片段开头才会显示该项但是你可以重写这个方法的行为比如你可以用子字符串来比较或是进行一些模糊比较
  • OnSelected - 这个方法会在文本插入文本框的时候调用你可以在这里对文本进行一些额外的操作比如你可以把光标移动到某处

控件库里还提供了几个从AutocompleteMenu派生的有用的类SnippetAutocompleteItem可以用于插入代码段MethodAutocompleteItem可以在点后面插入方法名称SubstringAutocompleteItem用子字符串来比较文本MulticolumnAutocompleteItem绘制多列菜单

使用源代码

简单用法

1) 把AutocompleteMenu组件扔到你的窗体上

2) 在AutocompleteMenu.Items里输入菜单项

就像这样

3) 设置你的文本框的AutocompleteMenu属性

就像这样

4) 搞定收工

高级用法

1) 把AutocompleteMenu组件扔到你的窗体上

2) 创建一个菜单项列表SetAutocompleteItems()或是AddItem()方法添加到菜单比如

 

1
2
3
4
5
6
7
8
9
10
11
12
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 });
 
    //设置为自动完成源
    autocompleteMenu1.SetAutocompleteItems(items);
}

同样你也可以添加自己的菜单项就是从AutocompleteItem继承而来的那种比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
    }
}

更多详细内容请参考Demo中的AdvancedSample例程

快捷键

你可以使用以下的快捷键

  • Ctrl+Space - 强制打开AutocompleteMenu
  • 上翻页下翻页 - 在菜单中来回移动
  • 回车Tab鼠标双击 - 插入选中的文本Tab键只在AllowTabKeytrue时才起作用
  • Esc - 关闭菜单

注意尽管窗体焦点位于文本框这些按键仍然哼正常工作

当你点选了菜单项就会显示相应的工具提示

自定义ListView

你可以用自定义控件来显示AutocompleteMenuListViewListBoxDataGridViewTreeView等等首先创建自己的控件Control类派生然后实现IAutocompleteListView接口更多详情请参考CustomListViewSample

动态上下文菜单

如果你要显示的菜单并非固定内容而是根据文本而动态改变那么你会经常用到这个部分

请注意菜单的SetAutocompleteItems()方法采用了IEnumerable接口作为要显示的菜单项集合参数

所以你不必在程序一开始就生成菜单项列表你只需要在调用菜单项的时候再动态生成就可以了

下面的代码演示了这个思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
autocompleteMenu1.SetAutocompleteItems(new DynamicCollection(tb));
    ....
 
    internal class DynamicCollection : IEnumerable<AutocompleteItem>
    {
        public IEnumerator<AutocompleteItem> GetEnumerator()
        {
            return BuildList().GetEnumerator();
        }
 
        private IEnumerable<AutocompleteItem> BuildList()
        {
            //找到文本中所有单词
            var words = new Dictionary<string, string>();
            foreach (Match m in Regex.Matches(tb.Text, @"\b\w+\b"))
                words[m.Value] = m.Value;
 
            //返回自动完成项
            foreach(var word in words.Keys)
                yield return new AutocompleteItem(word);
        }
    }

完整的实现代码请参考DynamicMenuSample

兼容性

自动完成菜单可以兼容TextBoxRichTextBoxMaskedTextBoxFastColoredTextBox一个非常强大的支持代码着色的文本框控件近期将对其进行翻译——野比注和其他派生自TextBoxBase的控件

同样自动完成菜单也兼容任何支持以下属性和方法的控件

  • string SelectedText{get;set;}
  • int SelectionLength{get;set;}
  • int SelectionStart{get;set;}
  • Point GetPositionFromCharIndex(int charPos)

即使你的控件不支持这些方法或属性你也可以为它创建自己的包装器要这样做你必须创建自己的包装类并实现ITextBoxWrapper接口

下面是ITextBoxWrapper的方法和属性

1
2
3
4
5
6
7
8
9
10
11
12
13
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附加到你的控件上去了就像这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
        //把<h-hws hidden=""> </h-hws>myControl1<h-hws hidden=""> </h-hws>附加到<h-hws hidden=""> </h-hws>autocompleteMenu1
            autocompleteMenu1.TargetControlWrapper = new MyControlWrapper(myControl1);
        }
    }
 
    internal class MyControlWrapper : ITextBoxWrapper
    {
        private MyControl tb;
 
        public MyControlWrapper(MyControl tb)
        {
            this.tb = tb;
        }
 
    //在这里实现<h-hws hidden=""> </h-hws>ITextBoxWrapper
    //(略)
    }

示例

Demo中包含了几个示例

SimplestSample - 展示最简单的使用控件方法

CustomItemSample - 展示了怎样创建从AutocompleteItem派生的类

AdvancedSample - 展示了怎样创建自定义的带关键字代码段方法提示文本纠错等的自动完成菜单

ExtraLargeSample - 演示了在极大量100菜单项情况下组件的性能

ComboboxSample - 展示了怎样创建模拟Combobox带特别大的下拉列表和子字符串搜索功能

MulticolumnSample - 展示了怎样制作多列自动完成菜单就像这样

CustomListViewSample - 展示了怎样在自动完成菜单中制作自定义ListView就像这样

DynamicMenuSample - 这个例子展示了怎样创建动态的上下文敏感的自动完成菜单

DataGridViewSample - 展示了怎样把AutocompleteMenu附加到DataGridView就像这样

历史

2012413日 - 首发

2012421日 - 重构了控件增加了对FastColoredTextBox和其他控件的支持

201259日 - 重构了控件增加了一些例子

许可

本文及相关源代码和文件均采用GNU通用公共许可证LGPLv3授权

 

全文完

© Written by Pavel Torgashov 2012, translated by Conmajia 2012

posted on2012-06-04   Conmajia  阅读(3359)  评论(0编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示