好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

自动完成菜单

自动完成菜单

「佳作赏析」自动完成菜单(Pavel Torgashov著,野比译)

 

自动完成菜单

[乌克兰]Pavel Torgashov著,野比译

自定义用于RichTextBox、TextBox和其他控件的自动完成菜单。

 codeproject.com「2012四月最佳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  - 工具提示标题。如果ToolTipTitle为null,则不会显示工具提示 ToolTipText  - 工具提示文本 Tag  - 你可在此附加任何数据

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

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

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

使用源代码

简单用法:

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

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

就像这样

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

就像这样

4) 搞定收工

高级用法:

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

2) 创建一个菜单项列表,用 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 });

 

     //设置为自动完成源

     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;

     }

}

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

快捷键:

你可以使用以下的快捷键:

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

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

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

自定义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()

         {

             //找到文本中所有单词

             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。

兼容性

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

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

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();

 

         //把myControl1附加到autocompleteMenu1

             autocompleteMenu1.TargetControlWrapper = new MyControlWrapper(myControl1);

         }

     }

 

     internal class MyControlWrapper : ITextBoxWrapper

     {

         private MyControl tb;

 

         public MyControlWrapper(MyControl tb)

         {

             this .tb = tb;

         }

 

     //在这里实现ITextBoxWrapper

     //(略)

     }

示例

Demo中包含了几个示例:

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

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

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

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

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

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

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

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

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

历史

2012年4月13日 - 首发。

2012年4月21日 - 重构了控件。增加了对FastColoredTextBox和其他控件的支持。

2012年5月9日 - 重构了控件。增加了一些例子。

许可

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

(本译文也如此——野比注)

关于作者

Pavel Torgashov

我是Pavel Torgashov。我住在乌克兰基辅市。
我从1998年就开始开发软件了。
我的联系email是:tp_soft[at]mail.ru

乌克兰

关于译者

野比「Conmajia」

我是野比,你也可以叫我Conmajia。
我从93年开始学长城机,开始各种玩。
各种玩。最大的兴趣是绘画和雕刻,
欢迎指导。

中国

(全文完)

 

标签:  C# ,  控件 ,  图形

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于自动完成菜单的详细内容...

  阅读:50次