[WinForm] 自动补全控件

在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;                        

 

posted @ 2013-02-13 16:24  Tony.J  阅读(733)  评论(1编辑  收藏  举报