读书人

「控件控」自动完成菜单、代码揭示、函

发布时间: 2012-10-24 14:15:58 作者: rapoo

「控件控」自动完成菜单、代码提示、函数列表、文本纠错等IntelliSense功能
题目很响亮吧。

我明后天出门郊游,闪人前从cp弄点好文过来,大家一起赏析。

个人认为这篇文章用好了能让你的程序直接跨入「专业」水准。至少「看起来」,但这就够了。

来点背景介绍,本文是乌克兰人Pavel Torgashov发表在codeproject上的文章,获得了今年"Best C# Article of April"的prize winner。

我已经征得Pavel本人同意,把这篇翻译出来,大家一起研究。

© Written by Pavel Torgashov 2012, translated by 野比「Conmajia」 2012

自动完成菜单
[乌克兰]Pavel Torgashov著,野比译
自定义用于RichTextBox、TextBox和其他控件的自动完成菜单。

点击阅读原文
下载源代码 - 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()方法添加到菜单。比如:

C# code
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继承而来的那种。比如:
C# code
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接口作为要显示的菜单项集合参数。
所以,你不必在程序一开始就生成菜单项列表。你只需要在调用菜单项的时候再动态生成就可以了。
下面的代码演示了这个思路:

C# code
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的方法和属性:
C# code
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附加到你的控件上去了。就像这样:

C# code
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

(全文完)

© Written by Pavel Torgashov 2012, translated by 野比「Conmajia」 2012

[解决办法]
好文,顶。
[解决办法]
虽然看不懂,感觉很NX
[解决办法]
大牛啊, 这个要收藏
[解决办法]
感觉很NX
[解决办法]
相当牛,学习,这家伙每天都这么闲
[解决办法]
这个要顶,看到控件控就进来了~~~~
[解决办法]
哎,,不知道这家伙是干啥的,,,太NB了,,
[解决办法]
野比发的帖子都很有技术含量啊...
[解决办法]
不错 要学习一下哦
[解决办法]
nx人,顶起
[解决办法]
利害呀。。。学习了
[解决办法]
这个不错~~
[解决办法]
很拉风
[解决办法]
不懂c&,但是我还是要收下
[解决办法]
看着不错
[解决办法]

探讨

好文,顶。

[解决办法]
这是我以前做的,效果比Pavel这个差远了,
[解决办法]

抱歉插成缩略图了= =
[解决办法]
好东西,不错!
[解决办法]
你是博客园、csdn双发哦
[解决办法]
积分怎么获取啊!!悲剧的
[解决办法]
牛的一逼!
[解决办法]
大牛啊, 这个要收藏
[解决办法]
效果不错,支持学习...
[解决办法]
刚好需要这东西来加强产品!感谢分享,顶啊
[解决办法]
好东西,收藏。
[解决办法]
好东西,收藏。
[解决办法]
感谢分享。
[解决办法]
C#。。。。。。。
[解决办法]
虽然学了点东西,但总体上还是没看懂什么意思???学起来很吃力
[解决办法]
运行成功了。。确实很厉害~~支持野比。
[解决办法]
非常感谢野哥的分享!!!
------解决方案--------------------


控件很不错
[解决办法]
不错顶一个,感情你天天都有这么多闲暇的时间!!!
[解决办法]
好吧,我虽然没看完,但知道这是个new B()
[解决办法]
好东西,顶一个
[解决办法]
虽然只知道是什么作用,但我知道这是个NX
[解决办法]
厉害啊啊啊 啊啊
[解决办法]
看不懂
[解决办法]
非常好,一直想实现这样的功能.
[解决办法]
神一样的!
[解决办法]
怎么用?看不懂啊?
[解决办法]
好东西,收藏了~
[解决办法]
好东西,顶顶
[解决办法]
写过下拉控件的就会懂得这个东西了.
不过匹配的算法都是各有不同的, 有时按顺序, 有时是包含, 有时按词, 有时按字母, 各种场合使用不同.
[解决办法]

     \(/_.-----_                        
    - \     \                        
    /  __    \   轻轻的我走了,  正如我轻轻的来;   
   (  ( (    \   我轻轻的招手,  作别西天的云彩。   
   /\ / `__\\\   /   那河畔的金柳,  是夕阳中的新娘。   
   \   @ \(  |    波光里的艳影,  在我的心头荡漾。   
    -^   | \)/ _   软泥上的青荇,  油油的在水底招摇;  
     | \ -- || //   在康河的柔波里, 我甘心做一条水草   
     | \  /||//   那树荫下的一潭, 不是清泉,是天上虹  
  ___- \  / / ~)   揉碎在浮藻间,  沉淀着彩虹似的梦。  
    \  \ / \  / _   寻梦?撑一支长篙,向青草更青处漫溯,  
 (   \   / / | \  满载一船星辉,  在星辉斑斓里放歌   
  \   \  /  /  | \  但我不能放歌,  悄悄是别离的笙箫;  
  \   /  /  |/  \ 夏虫也为我沉默, 沉默是今晚的康桥!  
   \     /  | \  \ 悄悄的我走了,  正如我悄悄的来;   
   | \   /   | /  / 我挥一挥衣袖,  不带走一片云彩。
[解决办法]

[解决办法]
不怎么看得懂诶
[解决办法]
真的很火啊
[解决办法]
很厉害啊 支持了
[解决办法]
谢谢大神的分享!!顶!!!
[解决办法]
感谢分享。
[解决办法]
感谢分享。。。
[解决办法]
这个要顶,看到控件控就进来了
[解决办法]

探讨

哎,,不知道这家伙是干啥的,,,太NB了,,

[解决办法]
探讨

引用:
\(/_.-----_
- \     \
/  __    \   轻轻的我走了,  正如我轻轻的来;
(  ( (    \  额 我轻轻的招手,  作别西天的云彩。
/\ / `__\\\   /   那河畔的金柳,  是夕阳中的新娘。
\   @ \(  |    波光里的艳影,  在我的心头荡漾。
-^   | \)/ _   软泥上的青荇,  ……

[解决办法]
new B 的 一B
正在看着李子模仿。。
[解决办法]
标题何止响亮,简直如雷贯耳。
------解决方案--------------------


好东西啊,支持lua吗?

读书人网 >C#

热点推荐