一、前言
又臨近春節,作為屌絲的我,又要為車票發愁了。記得去年出現了各種12306的插件,最近不忙,於是也着手自己寫個搶票插件,當是熟悉一下WinForm吧。小軟件還在開發中,待完善后,也寫篇博客與大家分享。今天我們的重點不是搶票軟件,而是其中的一點功能。我們在買票的時候選站點的自動補全如下圖:
這功能在WinForm里用什么控件來實現呢?
一、自帶控件
WinForm里面的ComBoBox 和TextBox 其實是有自帶的自動補全功能的,我們只需要將設置相應的屬性:
1、將 AutoCompleteSource 屬性設置為 ListItems 或 CustomerSource (textbox 沒有 ListItems)
2、設置 AutoCompleteMode 自動完成樣式屬性設置,有三值 Suggest(顯示相關下拉)、append(自動補全相關)、suggestappend(前兩者的結合),這個可以自行試驗下。
3、然后設置 綁定 控件的 DataSource 或 AutoCompleteCustomSource。
當AutoCompleteSource屬性設置的是 CustomerSource 的時候我們需要綁定 AutoCompleteCustomSource屬性的值,值為一個string類型的數組:
this.cbbEndStation.AutoCompleteCustomSource.AddRange(new string[] { "站點1", "站點2", "站點3", "站點4" });
這樣ComBoBox 和 TextBox 就有輸入提示功能了。至此,不知道大家有沒有發現問題,這里綁定的數據只有 顯示的值,而沒有 實際的值,一般像這種控件,我們都是有一個顯示值和一個實際值的。有人可能會說,使用ComBoBox 控件,然后將AutoCompleteSource設置為ListItems,提示的就是DataSource里的值了,而DataSource是可以綁定 集合,設置DisplayMember和ValueMember的。是的,這樣可以實現自動提示,並且也能在選中提示的某項時,取到顯示的值和實際值。但是這種方式至少有兩個缺點:
1、像購票的站點這種,數據量很大,有2k多條吧,你一次全綁定到ComboBox上?數據量太大,它沒有提供相應的事件來過濾數據。
2、多種搜索方式怎么辦?中文、拼音、實際值、都是是可以用來做輸入提示的關鍵字的。
其實以上兩點就是應為 沒有提供相應的事件來處理 “搜索”。
二、TextBox+ListBox 自定義AutoComplete
其實我可以用 TextBox來獲得用戶的輸入,然后動態控制ListBox。下面就按我做的思路一步步來實現一個自定義AutoComplete。
1、監聽 textbox的 keyUp事件,獲得用戶輸入

1 /// <summary> 2 /// 站點文本框 鍵盤按下松開事件 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void txtStation_KeyUp(object sender, KeyEventArgs e) 7 { 8 TextBox eObj = sender as TextBox; //事件源對象 9 txtStation_Name = eObj; //當前事件出發對象 10 if (eObj.Name == "txtStation_S_Name") 11 { 12 txtStation_Value = this.txtStation_S_Value; //保存值的textbox 13 ltb_Stations = this.lb_Start_Stations; //始發站 展示數據的 14 } 15 else 16 { 17 //到站 控件 18 txtStation_Value = this.txtStation_E_Value; //保存值的textbox 19 ltb_Stations = this.lb_End_Stations; //始發站 展示數據的 20 } 21 //上下左右 22 if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Left) 23 { 24 if (ltb_Stations.SelectedIndex > 0) 25 ltb_Stations.SelectedIndex--; 26 } 27 else if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Right) 28 { 29 if (ltb_Stations.SelectedIndex < ltb_Stations.Items.Count - 1) 30 ltb_Stations.SelectedIndex++; 31 } 32 //回車 33 else if (e.KeyCode == Keys.Enter) 34 { 35 StationInfo info = ltb_Stations.SelectedItem as StationInfo; 36 txtStation_Name.Text = info.StationName_CN; 37 txtStation_Value.Text = info.StationValue; 38 ltb_Stations.Visible = false; 39 } 40 else 41 { 42 43 if (txtStation_Name.Text != "") 44 { 45 IList<StationInfo> dataSource = StationInfo.GetStations(txtStation_Name.Text.Trim()); 46 if (dataSource.Count > 0) 47 { 48 ltb_Stations.DataSource = dataSource; 49 ltb_Stations.DisplayMember = "StationName_CN"; 50 ltb_Stations.ValueMember = "StationValue"; 51 ltb_Stations.Visible = true; 52 } 53 else 54 ltb_Stations.Visible = false; 55 } 56 else 57 { 58 ltb_Stations.Visible = false; 59 } 60 } 61 txtStation_Name.Select(txtStation_Name.Text.Length, 1); //光標定位到文本框最后 62 }
2、監聽 ListBox 控件的點擊事件
1 /// <summary> 2 /// 展示站點列表的listbox的點擊事件,為了給textbox賦值 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void ListBox_StationDatas_Click(object sender, EventArgs e) 7 { 8 ListBox eObj = sender as ListBox; 9 StationInfo info = eObj.SelectedItem as StationInfo; 10 txtStation_Name.Text = info.StationName_CN; 11 txtStation_Value.Text = info.StationValue; 12 eObj.Visible = false; 13 txtStation_Name.Select(txtStation_Name.Text.Length, 1); //光標定位到最后 14 }
3、監聽 ListBox 控件的鼠標移動事件
1 /// <summary> 2 /// 展示站點列表的listbox, 鼠標在該控件上移動事件, 3 /// 為了鼠標移動選項 4 /// </summary> 5 /// <param name="sender"></param> 6 /// <param name="e"></param> 7 private void ListBox_StationDatas_MouseMove(object sender, MouseEventArgs e) 8 { 9 ListBox eObj = sender as ListBox; 10 eObj.SelectedIndex = eObj.IndexFromPoint(e.Location); 11 }
以上三步就可以完成一個自定義 AutoComplete的功能了,為什么要叫自定義呢?因為我們在 監聽 TextBox 的輸入時,可以自定義搜索規則,還有我們可以將ListBox換成 DataGridView都是可以的,靈活性很大,只要按這個思路來就可以。
三、一些第三方控件
當然網絡上也有一些非常好的類似AutoComplete的第三方控件,這里我就不一一列出來了,因為我沒有找到合適的,呵呵!如果有人用過好的,歡迎在評論中分享,謝謝!
最后附上Demo的源碼:點擊這里下載!
demo截圖:
如發現文中有誤,或有更好的建議,歡迎指出!