介绍:
这是我的第一篇翻译文章,如有不当,欢迎指教。原文地址:
http://www.codeproject.com/KB/cs/AutoCompleteTextBox.aspx
在我工作当中的一个项目中我需要一个简单的自动填充TextBox,但是我在一大堆其它的方法中没有找到合适的。这些方法大部分都是继承自combobox,当你连续输入的时候,他的下拉列表不能连续的去掉一些条件。(翻译的不是很准确,原文:Many of them derive from a combo box which will not work properly for a dropdown list that continues to filter out items as you continue to type.)这是我的第一篇文章,所以欢迎探讨。
使用方法:
这个textbox的使用方法很简单,需加入一些AutoCompleteEntry 条件:
这些条件类集合继承于IAutoCompleteEntry
接口,它允许你使用AutoCompleteTextBox达到各种目的。IAutoCompleteEntry接口提供一个string数组来匹配用户的输入。下拉列表显示出结果。
// Add some sample auto complete entry items... this.coolTextBox1.Items.Add(new AutoCompleteEntry("Phoenix, Az", "Phoenix, Az", "Az", "PHX")); this.coolTextBox1.Items.Add(new AutoCompleteEntry("Tempe, Az", "Tempe, Az","Az", "TPE")); this.coolTextBox1.Items.Add(new AutoCompleteEntry("Chandler, Az", "Chandler, Az", "Az", "CHA")); this.coolTextBox1.Items.Add(new AutoCompleteEntry("Boxford, Ma", "Boxford, Ma", "Ma", "BXF")); this.coolTextBox1.Items.Add(new AutoCompleteEntry("Topsfield, Ma", "Topsfield, Ma", "Ma", "TPF")); this.coolTextBox1.Items.Add(new AutoCompleteEntry("Danvers, Ma", "Danvers, Ma", "Ma", "DNV"));
提示:我创建了一个控件,名叫CoolTextBox。在其中集成了AutoCompleteTextBox。他仅仅是为textbox实现了一个很酷的边框。我在demo中就是使用了CooltextBox。
设计:
我决定使用textbox,通过按键事件来显示结果弹出框。下拉框中只显示匹配用户输入的项目。
问题:
实现的问题集中在没有一个简单的方法实现大家所期望的弹出框模式。要显示弹出框很简单,但是我们需要集中焦点在textbox,更新弹出框的数据,甚至在弹出框实现按键响应。还有当鼠标点击在弹出框外或者textbox外时,我们需要隐藏弹出框。(我知道这听起来似乎很简单)。
执行:
控制弹出:
我喜欢写一些一般化和与用户交互方便的控件。我喜欢我的autocomplete textbox在我输入两个字符,或者我按下Control+Space组合键的时候弹出我希望的列表。一些人或许有完全不同的想法,所以我决定使用AutoCompleteTrigger
对象来控制下拉框的事件。
下面是默认属性的设置代码(那个带“Consume”的阻止更进一步的按键事件)。
// this.triggers.Add(new TextLengthTrig Add default triggers.ger(2)); this.triggers.Add(new ShortCutTrigger(Keys.Enter, TriggerState.SelectAndConsume)); this.triggers.Add(new ShortCutTrigger(Keys.Tab, TriggerState.Select)); this.triggers.Add(new ShortCutTrigger(Keys.Control | Keys.Space, TriggerState .ShowAndConsume)); this.triggers.Add(new ShortCutTrigger(Keys.Escape, TriggerState.HideAndConsume));
关闭弹出框的情况:
在下面的任意情况下,下拉框都会关闭:
1、 用户点击任何一项时
2、 下面的任一情况触发时:
a)
TriggerState.Select
b) TriggerState.SelectAndConsume
c) TriggerState.Hide
d) TriggerState.HideAndConsume
3、当用户点击不在弹出框和textbox范围的地方。
第1和第2种情况相当的简单,我就不在这里废话了。如果有兴趣你可以下载源代码。第三种情况的实现确实是一个挑战。
处理鼠标点击:
这里有几个地方我们需要时刻抓住,以便可以计算出鼠标是否点击在textbox或者弹出框外。
我们需要捕获textbox的OnLostFocus,和弹出框的Deactivate事件。
protected override void OnLostFocus(EventArgs e) { base.OnLostFocus (e); if (!(this.Focused || this.popup.Focused || this.list.Focused)) { this.HideList(); } } private void Popup_Deactivate(object sender, EventArgs e) { if (!(this.Focused || this.popup.Focused || this.list.Focused)) { this.HideList(); } }
这些都是简单的。现在我们需要捕获所有的发生在我们的textbox所在的窗体,及其子控件的鼠标事件。这确实有点难度。为了实现它我使用了NativeWindow的基类来实现鼠标的挂钩。接下来我监听所有的鼠标事件。当鼠标单击在textbox范围内时,弹出框依然出现,如果不是,那么就隐藏弹出框。
/// <summary> /// This is the class we will use to hook mouse events. /// </summary> private class WinHook : NativeWindow { private AutoCompleteTextBox tb; /// <summary> /// Initializes a new instance of <see cref="WinHook"/> /// </summary> /// <param name="tbox">The <see cref="AutoCompleteTextBox"/> the hook is running /// for.</param> public WinHook(AutoCompleteTextBox tbox) { this.tb = tbox; } /// <summary> /// Look for any kind of mouse activity that is not in the /// text box itself, and hide the popup if it is visible. /// </summary> /// <param name="m"></param> protected override void WndProc(ref Message m) { switch (m.Msg) { case Win32.Messages.WM_LBUTTONDOWN: case Win32.Messages.WM_LBUTTONDBLCLK: case Win32.Messages.WM_MBUTTONDOWN: case Win32.Messages.WM_MBUTTONDBLCLK: case Win32.Messages.WM_RBUTTONDOWN: case Win32.Messages.WM_RBUTTONDBLCLK: case Win32.Messages.WM_NCLBUTTONDOWN: case Win32.Messages.WM_NCMBUTTONDOWN: case Win32.Messages.WM_NCRBUTTONDOWN: { // Lets check to see where the event took place Form form = tb.FindForm(); Point p = form.PointToScreen(new Point((int)m.LParam)); Point p2 = tb.PointToScreen(new Point(0, 0)); Rectangle rect = new Rectangle(p2, tb.Size); // Hide the popup if it is not in the text box if (!rect.Contains(p)) { tb.HideList(); } } break; case Win32.Messages.WM_SIZE: case Win32.Messages.WM_MOVE: { tb.HideList(); } break; // This is the message that gets sent when a child control gets activity case Win32.Messages.WM_PARENTNOTIFY: { switch ((int)m.WParam) { case Win32.Messages.WM_LBUTTONDOWN: case Win32.Messages.WM_LBUTTONDBLCLK: case Win32.Messages.WM_MBUTTONDOWN: case Win32.Messages.WM_MBUTTONDBLCLK: case Win32.Messages.WM_RBUTTONDOWN: case Win32.Messages.WM_RBUTTONDBLCLK: case Win32.Messages.WM_NCLBUTTONDOWN: case Win32.Messages.WM_NCMBUTTONDOWN: case Win32.Messages.WM_NCRBUTTONDOWN: { // Same thing as before Form form = tb.FindForm(); Point p = form.PointToScreen(new Point((int)m.LParam)); Point p2 = tb.PointToScreen(new Point(0, 0)); Rectangle rect = new Rectangle(p2, tb.Size); if (!rect.Contains(p)) { tb.HideList(); } } break; } } break; } base.WndProc (ref m); } }
结论:
当然这里还有很多的细节,比如如何把这些整合到一块。不过这些都是比较直观的。我觉得对于自动填充TextBox这是一个可行的解决方案。希望大家有兴趣。
源文件已上传到资源,http://download.csdn.net/source/2930345 Demo和源代码。