在Web的应用方面有js的插件实现自动完成(或叫智能提示)功能,但在WinForm窗体应用方面就没那么好了。
TextBox控件本身是提供了一个自动提示功能,只要用上这三个属性:
AutoCompleteCustomSource:AutoCompleteSource 属性设置为CustomSource 时要使用的 StringCollection。
AutoCompleteMode: 指示文本框的文本完成行为。
AutoCompleteSource:自动完成源,可以是 AutoCompleteSource 的枚举值之一。
就行了, 一个简单的示例如下
textBox1.AutoCompleteCustomSource .AddRange(new string[] { "java","javascript","js","c#","c","c++" }); textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
可是这种方式的不支持我们中文的简拼自动完成(如在文本框里输入"gz"就会出现"广州")。只好自己写一个支持简拼自动完成的控件了。
这是效果图
控件不太复杂,一个TextBox和一个ListBox。代码方面,用DataTable作数据源,每次在TextBox的值时,通过DataTable的Select方法,配上合适的表达式(如:{0} like '{1}%' and IsNull([{2}], ' ') <> ' ')来筛选出合适的备选文本内容,以下则是控件的代码:
变量
1 private TextBox _tb; 2 private ListBox _lb; 3 private DataTable _dt_datasource; 4 private bool _text_lock; 5 private string _general_text;//原始输入文本框的值 6 private bool _lb_kd_first_top;//listbox是否第一次到达顶部 7 private int _itemCount;
属性
1 /// <summary> 2 /// TextBox的Text属性,增加了_text_lock操作,放置触发TextChanged事件 3 /// </summary> 4 private string TextBoxText 5 { 6 get { return _tb.Text; } 7 set 8 { 9 _text_lock = true; 10 _tb.Text = value; 11 _text_lock = false; 12 } 13 } 14 15 /// <summary> 16 /// 显示在ListBox的字段名 17 /// </summary> 18 public string ValueName { get; set; } 19 20 /// <summary> 21 /// 用于匹配的字段名 22 /// </summary> 23 public string CodeName { get; set; } 24 25 /// <summary> 26 /// 显示提示项的数量 27 /// </summary> 28 public int ItemCount 29 { 30 get 31 { return _itemCount; } 32 set 33 { 34 if (value <= 0) 35 _itemCount = 1; 36 else 37 _itemCount = value; 38 } 39 } 40 41 public DataTable DataSource 42 { 43 get { return _dt_datasource; } 44 set { _dt_datasource = value; } 45 }
构造函数
1 public AutoComplete() 2 { 3 InitialControls(); 4 }
控件事件
1 void AutoComplete_Load(object sender, EventArgs e) 2 { 3 _tb.Width = this.Width; 4 _lb.Width = _tb.Width; 5 this.Height = _tb.Height-1; 6 } 7 8 void AutoComplete_LostFocus(object sender, EventArgs e) 9 { 10 _lb.Visible = false; 11 this.Height = _tb.Height-1; 12 }
列表框事件
1 //列表框按键事件 2 void _lb_KeyDown(object sender, KeyEventArgs e) 3 { 4 if (_lb.Items.Count == 0 || !_lb.Visible) return; 5 6 if (!_lb_kd_first_top && ((e.KeyCode == Keys.Up && _lb.SelectedIndex == 0) || (e.KeyCode == Keys.Down && _lb.SelectedIndex == _lb.Items.Count))) 7 { 8 _lb.SelectedIndex = -1; 9 TextBoxText = _general_text; 10 } 11 else 12 { 13 TextBoxText = ((DataRowView)_lb.SelectedItem)[ValueName].ToString(); 14 _lb_kd_first_top = _lb.SelectedIndex != 0; 15 } 16 17 if (e.KeyCode == Keys.Enter && _lb.SelectedIndex != -1) 18 { 19 _lb.Visible = false; 20 this.Height = _tb.Height; 21 _tb.Focus(); 22 } 23 } 24 25 //列表鼠标单击事件 26 void _lb_Click(object sender, EventArgs e) 27 { 28 if (_lb.SelectedIndex != -1) 29 { 30 TextBoxText = ((DataRowView)_lb.SelectedItem)[ValueName].ToString(); 31 } 32 _lb.Visible = false; 33 _tb.Focus(); 34 this.Height = _tb.Height; 35 }
文本框事件
1 //文本框按键事件 2 void _tb_KeyDown(object sender, KeyEventArgs e) 3 { 4 if (_lb.Items.Count == 0||!_lb.Visible) return; 5 6 bool _is_set = false; 7 8 if (e.KeyCode == Keys.Up) 9 { 10 if (_lb.SelectedIndex <= 0) 11 { 12 _lb.SelectedIndex = -1; 13 TextBoxText = _general_text; 14 } 15 else 16 { 17 _lb.SelectedIndex--; 18 _is_set = true; 19 } 20 } 21 else if (e.KeyCode == Keys.Down) 22 { 23 if (_lb.SelectedIndex == _lb.Items.Count - 1) 24 { 25 _lb.SelectedIndex = 0; 26 _lb.SelectedIndex = -1; 27 TextBoxText = _general_text; 28 } 29 else 30 { 31 _lb.SelectedIndex++; 32 _is_set = true; 33 } 34 } 35 else if (e.KeyCode == Keys.Enter) 36 { 37 _lb.Visible = false; 38 this.Height = _tb.Height; 39 _is_set = _lb.SelectedIndex != -1; 40 } 41 42 _lb_kd_first_top = _lb.SelectedIndex != 0; 43 if (_is_set) 44 { 45 _text_lock = true; 46 _tb.Text = ((DataRowView)_lb.SelectedItem)[ValueName].ToString(); 47 _tb.SelectionStart = _tb.Text.Length + 10; 48 _tb.SelectionLength = 0; 49 _text_lock = false; 50 } 51 } 52 53 //文本框文本变更事件 54 void _tb_TextChanged(object sender, EventArgs e) 55 { 56 if (_text_lock) return; 57 _general_text = _tb.Text; 58 _lb.Visible = true; 59 _lb.Height = _lb.ItemHeight * (_itemCount+1); 60 this.BringToFront(); 61 _lb.BringToFront(); 62 this.Height = _tb.Height + _lb.Height; 63 64 DataTable temp_table = _dt_datasource.Clone(); 65 string filtStr = FormatStr(_tb.Text); 66 DataRow [] rows = _dt_datasource.Select(string.Format(GetFilterStr(),CodeName,filtStr,_lb.DisplayMember)); 67 for (int i = 0; i < rows.Length&&i<_itemCount; i++) 68 { 69 temp_table.Rows.Add(rows[i].ItemArray); 70 } 71 _lb.DataSource = temp_table; 72 if (_lb.Items.Count > 0) _lb.SelectedItem = _lb.Items[0]; 73 }
方法
1 /// <summary> 2 /// 初始化控件 3 /// </summary> 4 private void InitialControls() 5 { 6 _lb_kd_first_top = true; 7 8 _tb = new TextBox(); 9 _tb.Location = new Point(0, 0); 10 _tb.Margin = new System.Windows.Forms.Padding(0); 11 _tb.Width = this.Width; 12 _tb.TextChanged += new EventHandler(_tb_TextChanged); 13 _tb.KeyUp += new KeyEventHandler(_tb_KeyDown); 14 15 _lb = new ListBox(); 16 _lb.Visible = false; 17 _lb.Width = _tb.Width; 18 _lb.Margin = new System.Windows.Forms.Padding(0); 19 _lb.DisplayMember = ValueName; 20 _lb.SelectionMode = SelectionMode.One; 21 _lb.Location = new Point(0, _tb.Height); 22 _lb.KeyUp += new KeyEventHandler(_lb_KeyDown); 23 _lb.Click += new EventHandler(_lb_Click); 24 25 this.Controls.Add(_tb); 26 this.Controls.Add(_lb); 27 this.Height = _tb.Height - 1; 28 this.LostFocus += new EventHandler(AutoComplete_LostFocus); 29 this.Leave += new EventHandler(AutoComplete_LostFocus); 30 this.Load += new EventHandler(AutoComplete_Load); 31 } 32 33 /// <summary> 34 /// 获取过滤格式字符串 35 /// </summary> 36 /// <returns></returns> 37 private string GetFilterStr() 38 { 39 //未过滤注入的字符 ' ] %任意 *任意 40 string filter = " {0} like '{1}%' and IsNull([{2}], ' ') <> ' ' "; 41 if (_dt_datasource.Rows[0][CodeName].ToString().LastIndexOf(';') > -1) 42 filter = " {0} like '%;{1}%' and IsNull([{2}],' ') <> ' ' "; 43 44 return filter; 45 } 46 47 /// <summary> 48 /// 过滤字符串中一些可能造成出错的字符 49 /// </summary> 50 /// <param name="str"></param> 51 /// <returns></returns> 52 private string FormatStr(string str) 53 { 54 if (string.IsNullOrEmpty(str)) return string.Empty; 55 str = str.Replace("[", "[[]").Replace("%", "[%]").Replace("*", "[*]").Replace("'", "''"); 56 if (CodeName == "code") str = str.Replace(" ", ""); 57 return str; 58 }
下面是使用控件的例子
class Common { /// <summary> /// 生成测试数据源 /// </summary> public static DataTable CreateTestDataSoucre { get { List<KeyValuePair<string, string>> source = new List<KeyValuePair<string, string>>() { new KeyValuePair<string,string>("张三",";zs;张三;"), new KeyValuePair<string,string>("李四",";li;李四;"), new KeyValuePair<string,string>("王五",";ww;王五;"), new KeyValuePair<string,string>("赵六",";zl;赵六;"), new KeyValuePair<string,string>("洗刷",";cs;csharp;c#;洗刷;"), new KeyValuePair<string,string>("爪哇",";java;爪哇;"), new KeyValuePair<string,string>("java",";java;"), new KeyValuePair<string,string>("c#",";c#;cs;csharp;"), new KeyValuePair<string,string>("javascript",";javascript;js;") }; DataTable table = new DataTable(); table.Columns.Add("id"); table.Columns.Add("name"); table.Columns.Add("code"); for (int i = 0; i < source.Count; i++) { DataRow row = table.Rows.Add(); row["id"] = i; row["name"] = source[i].Key; row["code"] = source[i].Value; } return table; } } } //............. AutoComplete ac=new AutoComplete(); ac.ValueName = "name"; ac.CodeName = "code"; ac.DataSource= Common.CreateTestDataSoucre; ac.ItemCount= 5;