C#自动完成

我正在尝试将自动完成function添加到文本框,结果来自数据库。 他们来的格式是

最后,第一中间

目前您必须input[001] …才能显示条目。 所以问题是我想要它完成,即使我先input名字 。 所以,如果一个入口是

史密斯,约翰D

如果我开始input约翰,那么这个条目应该显示在自动完成的结果。

目前代码看起来像

AutoCompleteStringCollection acsc = new AutoCompleteStringCollection(); txtBox1.AutoCompleteCustomSource = acsc; txtBox1.AutoCompleteMode = AutoCompleteMode.Suggest; txtBox1.AutoCompleteSource = AutoCompleteSource.CustomSource; .... if (results.Rows.Count > 0) for (int i = 0; i < results.Rows.Count && i < 10; i++) { row = results.Rows[i]; acsc.Add(row["Details"].ToString()); } } 

结果是包含查询结果的数据集

该查询是使用类似语句的简单search查询。 如果我们不使用自动完成并将结果扔到数组中,则会返回正确的结果。

有什么build议?

编辑:

这是返回结果的查询

 SELECT Name from view_customers where Details LIKE '{0}' 

{0}是searchstring的占位符。

现有的自动完成function仅支持按前缀search。 似乎没有任何体面的方式来覆盖行为。

有些人通过重写OnTextChanged事件来实现自己的自动完成function。 这可能是你最好的select。

例如,您可以在TextBox正下方添加一个ListBox ,并将其默认可见性设置为false。 然后,您可以使用TextBoxOnTextChanged事件和ListBoxSelectedIndexChanged事件来显示和select项目。

作为一个简单的例子,这似乎很好:

 public Form1() { InitializeComponent(); acsc = new AutoCompleteStringCollection(); textBox1.AutoCompleteCustomSource = acsc; textBox1.AutoCompleteMode = AutoCompleteMode.None; textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource; } private void button1_Click(object sender, EventArgs e) { acsc.Add("[001] some kind of item"); acsc.Add("[002] some other item"); acsc.Add("[003] an orange"); acsc.Add("[004] i like pickles"); } void textBox1_TextChanged(object sender, System.EventArgs e) { listBox1.Items.Clear(); if (textBox1.Text.Length == 0) { hideResults(); return; } foreach (String s in textBox1.AutoCompleteCustomSource) { if (s.Contains(textBox1.Text)) { Console.WriteLine("Found text in: " + s); listBox1.Items.Add(s); listBox1.Visible = true; } } } void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) { textBox1.Text = listBox1.Items[listBox1.SelectedIndex].ToString(); hideResults(); } void listBox1_LostFocus(object sender, System.EventArgs e) { hideResults(); } void hideResults() { listBox1.Visible = false; } 

没有太多的努力就可以做更多的事情:将文本附加到文本框中,捕获更多的键盘命令等等。

如果您决定使用基于用户input的查询,请确保使用SqlParameters来避免SQL注入攻击

 SqlCommand sqlCommand = new SqlCommand(); sqlCommand.CommandText = "SELECT Name from view_customers where Details LIKE '%" + @SearchParam + "%'"; sqlCommand.Parameters.AddWithValue("@SearchParam", searchParam); 

这是一个inheritanceComboBox控件类的实现,而不是用一个新的控件replace整个combobox。 当你在文本框中input时,它会显示它自己的下拉菜单,但是点击来显示下拉列表是按照以前的方式处理的(即不用这个代码)。 因此,你可以得到适当的本地控制和外观。

请使用它,修改它并编辑答案,如果你想改善它!

 class ComboListMatcher : ComboBox, IMessageFilter { private Control ComboParentForm; // Or use type "Form" private ListBox listBoxChild; private int IgnoreTextChange; private bool MsgFilterActive = false; public ComboListMatcher() { // Set up all the events we need to handle TextChanged += ComboListMatcher_TextChanged; SelectionChangeCommitted += ComboListMatcher_SelectionChangeCommitted; LostFocus += ComboListMatcher_LostFocus; MouseDown += ComboListMatcher_MouseDown; HandleDestroyed += ComboListMatcher_HandleDestroyed; } void ComboListMatcher_HandleDestroyed(object sender, EventArgs e) { if (MsgFilterActive) Application.RemoveMessageFilter(this); } ~ComboListMatcher() { } private void ComboListMatcher_MouseDown(object sender, MouseEventArgs e) { HideTheList(); } void ComboListMatcher_LostFocus(object sender, EventArgs e) { if (listBoxChild != null && !listBoxChild.Focused) HideTheList(); } void ComboListMatcher_SelectionChangeCommitted(object sender, EventArgs e) { IgnoreTextChange++; } void InitListControl() { if (listBoxChild == null) { // Find parent - or keep going up until you find the parent form ComboParentForm = this.Parent; if (ComboParentForm != null) { // Setup a messaage filter so we can listen to the keyboard if (!MsgFilterActive) { Application.AddMessageFilter(this); MsgFilterActive = true; } listBoxChild = listBoxChild = new ListBox(); listBoxChild.Visible = false; listBoxChild.Click += listBox1_Click; ComboParentForm.Controls.Add(listBoxChild); ComboParentForm.Controls.SetChildIndex(listBoxChild, 0); // Put it at the front } } } void ComboListMatcher_TextChanged(object sender, EventArgs e) { if (IgnoreTextChange > 0) { IgnoreTextChange = 0; return; } InitListControl(); if (listBoxChild == null) return; string SearchText = this.Text; listBoxChild.Items.Clear(); // Don't show the list when nothing has been typed if (!string.IsNullOrEmpty(SearchText)) { foreach (string Item in this.Items) { if (Item != null && Item.Contains(SearchText, StringComparison.CurrentCultureIgnoreCase)) listBoxChild.Items.Add(Item); } } if (listBoxChild.Items.Count > 0) { Point PutItHere = new Point(this.Left, this.Bottom); Control TheControlToMove = this; PutItHere = this.Parent.PointToScreen(PutItHere); TheControlToMove = listBoxChild; PutItHere = ComboParentForm.PointToClient(PutItHere); TheControlToMove.Show(); TheControlToMove.Left = PutItHere.X; TheControlToMove.Top = PutItHere.Y; TheControlToMove.Width = this.Width; int TotalItemHeight = listBoxChild.ItemHeight * (listBoxChild.Items.Count + 1); TheControlToMove.Height = Math.Min(ComboParentForm.ClientSize.Height - TheControlToMove.Top, TotalItemHeight); } else HideTheList(); } /// <summary> /// Copy the selection from the list-box into the combo box /// </summary> private void CopySelection() { if (listBoxChild.SelectedItem != null) { this.SelectedItem = listBoxChild.SelectedItem; HideTheList(); this.SelectAll(); } } private void listBox1_Click(object sender, EventArgs e) { var ThisList = sender as ListBox; if (ThisList != null) { // Copy selection to the combo box CopySelection(); } } private void HideTheList() { if (listBoxChild != null) listBoxChild.Hide(); } public bool PreFilterMessage(ref Message m) { if (m.Msg == 0x201) // Mouse click: WM_LBUTTONDOWN { var Pos = new Point((int)(m.LParam.ToInt32() & 0xFFFF), (int)(m.LParam.ToInt32() >> 16)); var Ctrl = Control.FromHandle(m.HWnd); if (Ctrl != null) { // Convert the point into our parent control's coordinates ... Pos = ComboParentForm.PointToClient(Ctrl.PointToScreen(Pos)); // ... because we need to hide the list if user clicks on something other than the list-box if (ComboParentForm != null) { if (listBoxChild != null && (Pos.X < listBoxChild.Left || Pos.X > listBoxChild.Right || Pos.Y < listBoxChild.Top || Pos.Y > listBoxChild.Bottom)) { this.HideTheList(); } } } } else if (m.Msg == 0x100) // WM_KEYDOWN { if (listBoxChild != null && listBoxChild.Visible) { switch (m.WParam.ToInt32()) { case 0x1B: // Escape key this.HideTheList(); return true; case 0x26: // up key case 0x28: // right key // Change selection int NewIx = listBoxChild.SelectedIndex + ((m.WParam.ToInt32() == 0x26) ? -1 : 1); // Keep the index valid! if (NewIx >= 0 && NewIx < listBoxChild.Items.Count) listBoxChild.SelectedIndex = NewIx; return true; case 0x0D: // return (use the currently selected item) CopySelection(); return true; } } } return false; } } 

如果您正在运行该查询(将{0}replace为input的string),则可能需要:

 SELECT Name from view_customers where Details LIKE '%{0}%' 

LIKE仍然需要%字符…是的,你应该使用参数,而不是相信用户的input:)

此外,您似乎正在返回Name列,但查询Details列。 所以如果有人input“John Smith”,如果这不在“ Details列中,则不会得到您想要的结果。

这将给你自动完成你正在寻找的行为。

附加的例子是一个完整的工作表单,只需要你的数据源,并绑定列名。

 using System; using System.Data; using System.Windows.Forms; public partial class frmTestAutocomplete : Form { private DataTable maoCompleteList; private const string MC_DISPLAY_COL = "name"; private const string MC_ID_COL = "id"; public frmTestAutocomplete() { InitializeComponent(); } private void frmTestAutocomplete_Load(object sender, EventArgs e) { using (clsDataAccess oData = new clsDataAccess()) { maoCompleteList = oData.PurificationRuns; maoCompleteList.CaseSensitive = false; //turn off case sensitivity for searching testCombo.DisplayMember = MC_DISPLAY_COL; testCombo.ValueMember = MC_ID_COL; testCombo.DataSource = GetDataTableFromDatabase(); testCombo.SelectedIndexChanged += testCombo_SelectedIndexChanged; testCombo.KeyUp += testCombo_KeyUp; } } private void testCombo_KeyUp(object sender, KeyEventArgs e) { //use keyUp event, as text changed traps too many other evengts. ComboBox oBox = (ComboBox)sender; string sBoxText = oBox.Text; DataRow[] oFilteredRows = maoCompleteList.Select(MC_DISPLAY_COL + " Like '%" + sBoxText + "%'"); DataTable oFilteredDT = oFilteredRows.Length > 0 ? oFilteredRows.CopyToDataTable() : maoCompleteList; //NOW THAT WE HAVE OUR FILTERED LIST, WE NEED TO RE-BIND IT WIHOUT CHANGING THE TEXT IN THE ComboBox. //1).UNREGISTER THE SELECTED EVENT BEFORE RE-BINDING, b/c IT TRIGGERS ON BIND. testCombo.SelectedIndexChanged -= testCombo_SelectedIndexChanged; //don't select on typing. oBox.DataSource = oFilteredDT; //2).rebind to filtered list. testCombo.SelectedIndexChanged += testCombo_SelectedIndexChanged; //3).show the user the new filtered list. oBox.DroppedDown = true; //do this before repainting the text, as it changes the dropdown text. //4).binding data source erases text, so now we need to put the user's text back, oBox.Text = sBoxText; oBox.SelectionStart = sBoxText.Length; //5). need to put the user's cursor back where it was. } private void testCombo_SelectedIndexChanged(object sender, EventArgs e) { ComboBox oBox = (ComboBox)sender; if (oBox.SelectedValue != null) { MessageBox.Show(string.Format(@"Item #{0} was selected.", oBox.SelectedValue)); } } } //===================================================================================================== // code from frmTestAutocomplete.Designer.cs //===================================================================================================== partial class frmTestAutocomplete { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.testCombo = new System.Windows.Forms.ComboBox(); this.SuspendLayout(); // // testCombo // this.testCombo.FormattingEnabled = true; this.testCombo.Location = new System.Drawing.Point(27, 51); this.testCombo.Name = "testCombo"; this.testCombo.Size = new System.Drawing.Size(224, 21); this.testCombo.TabIndex = 0; // // frmTestAutocomplete // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(292, 273); this.Controls.Add(this.testCombo); this.Name = "frmTestAutocomplete"; this.Text = "frmTestAutocomplete"; this.Load += new System.EventHandler(this.frmTestAutocomplete_Load); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox testCombo; } 

有两种方法在使用SQL的autoComplete文本框控件中成功完成:

但是你应该做以下的事情:

a-创build新项目

b-添加组件类来投影和删除component1.designer“根据你给组件类”

c – 下载“下载示例 – 144.82 KB”并打开它并从AutoCompleteTextbox.cs中打开AutoCompleteTextbox类
d-select全部如图所示,并将其复制到当前组件类

http://i.stack.imgur.com/oSqCa.png

e-最终 – 运行项目,停止在toolBox中查看新的AutoCompleteTextbox。

现在您可以添加以下两种方法,您可以使用SQL

1-在Form_Load中

 private void Form1_Load(object sender, EventArgs e) { SqlConnection cn = new SqlConnection(@"server=.;database=My_dataBase;integrated security=true"); SqlDataAdapter da = new SqlDataAdapter(@"SELECT [MyColumn] FROM [my_table]", cn); DataTable dt = new DataTable(); da.Fill(dt); List<string> myList = new List<string>(); foreach (DataRow row in dt.Rows) { myList.Add((string)row[0]); } autoCompleteTextbox1.AutoCompleteList = myList; } 

2-在TextChanged事件

  private void autoCompleteTextbox_TextChanged(object sender, EventArgs e) { SqlConnection cn = new SqlConnection(@"server=.;database=My_dataBase;integrated security=true"); SqlDataAdapter da = new SqlDataAdapter(@"SELECT [MyColumn] FROM [my_table]", cn); DataTable dt = new DataTable(); da.Fill(dt); List<string> myList = new List<string>(); foreach (DataRow row in dt.Rows) { myList.Add((string)row[0]); } autoCompleteTextbox2.AutoCompleteList = myList; }