C # AutoComplete

J’essaie d’append une fonctionnalité de saisie semi-automatique à une zone de texte, les résultats proviennent d’une firebase database. Ils viennent dans le format de

[001] Last, First Middle

Actuellement, vous devez saisir [001] … pour afficher les entrées. Donc, le problème est que je veux le terminer même si je tape le prénom en premier . Donc, si une entrée était

[001] Smith, John D

Si j’ai commencé à taper John, cette entrée devrait apparaître dans les résultats de la saisie automatique.

Actuellement, le code ressemble à quelque chose

AutoCompleteSsortingngCollection acsc = new AutoCompleteSsortingngCollection(); 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()); } } 

resultats est un dataset contenant les résultats de la requête

La requête est une requête de recherche simple utilisant l’instruction like. Les résultats corrects sont renvoyés si nous n’utilisons pas la saisie semi-automatique et que nous lançons simplement les résultats dans un tableau.

Aucun conseil?

MODIFIER:

Voici la requête qui renvoie les résultats

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

Avec {0} étant l’espace réservé pour la chaîne recherchée.

La fonctionnalité de saisie semi-automatique existante ne prend en charge que la recherche par préfixe. Il ne semble pas y avoir de moyen décent de remplacer le comportement.

Certaines personnes ont implémenté leurs propres fonctions de OnTextChangedOnTextChanged événement OnTextChanged . C’est probablement votre meilleur pari.

Par exemple, vous pouvez append un ListBox juste en dessous du TextBox et définir sa visibilité par défaut sur false. Vous pouvez ensuite utiliser l’événement OnTextChanged de TextBox et l’événement SelectedIndexChanged du ListBox pour afficher et sélectionner des éléments.

Cela semble fonctionner assez bien comme un exemple rudimentaire:

 public Form1() { InitializeComponent(); acsc = new AutoCompleteSsortingngCollection(); 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 (Ssortingng 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].ToSsortingng(); hideResults(); } void listBox1_LostFocus(object sender, System.EventArgs e) { hideResults(); } void hideResults() { listBox1.Visible = false; } 

Vous pouvez faire beaucoup plus sans trop d’efforts: ajoutez du texte à la zone de texte, capturez des commandes clavier supplémentaires, etc.

Si vous décidez d’utiliser une requête basée sur une entrée utilisateur, veillez à utiliser SqlParameters pour éviter les attaques par injection SQL.

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

Voici une implémentation qui hérite de la classe de contrôle ComboBox , plutôt que de remplacer la boîte à options entière par un nouveau contrôle. Il affiche sa propre liste déroulante lorsque vous tapez dans la zone de texte, mais en cliquant pour afficher la liste déroulante est géré comme précédemment (c’est-à-dire pas avec ce code). En tant que tel, vous obtenez ce contrôle et cet aspect natifs.

Veuillez l’utiliser, modifiez-le et modifiez la réponse si vous souhaitez l’améliorer!

 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; ssortingng SearchText = this.Text; listBoxChild.Items.Clear(); // Don't show the list when nothing has been typed if (!ssortingng.IsNullOrEmpty(SearchText)) { foreach (ssortingng Item in this.Items) { if (Item != null && Item.Contains(SearchText, SsortingngComparison.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(); } ///  /// Copy the selection from the list-box into the combo box ///  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; } } 

Si vous exécutez cette requête (avec {0} remplacé par la chaîne entrée), vous devrez peut-être:

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

LIKE toujours besoin du caractère % … Et oui, vous devriez utiliser des parameters plutôt que de faire confiance à la saisie de l’utilisateur 🙂

En outre, vous semblez renvoyer la colonne Name , mais en interrogeant la colonne Details . Donc, si quelqu’un tape “John Smith”, si ce n’est pas dans la colonne Details , vous n’obtiendrez pas ce que vous voulez.

CECI VOUS DONNERA LE COMPORTEMENT AUTOCOMPLETE QUE VOUS RECHERCHEZ.

L’exemple ci-joint est un formulaire de travail complet. Il suffit de votre source de données et des noms de colonne liés.

 using System; using System.Data; using System.Windows.Forms; public partial class frmTestAutocomplete : Form { private DataTable maoCompleteList; private const ssortingng MC_DISPLAY_COL = "name"; private const ssortingng 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; ssortingng 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(ssortingng.Format(@"Item #{0} was selected.", oBox.SelectedValue)); } } } //===================================================================================================== // code from frmTestAutocomplete.Designer.cs //===================================================================================================== partial class frmTestAutocomplete { ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  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; } 

Deux méthodes ont été utilisées avec succès dans le contrôle autoComplete textBox avec SQL:

mais vous devriez faire ce qui suit:

a- créer un nouveau projet

b- Ajouter la classe Component au projet et supprimer le composant1.designer “en fonction du nom que vous donnez à la classe du composant”

c-télécharger “Télécharger exemple – 144.82 Ko” et ouvrez-le et ouvrez la classe AutoCompleteTextbox à partir de AutoCompleteTextbox.cs
d- sélectionner tout comme illustré dans l’image et le copier dans la classe de composant actuelle

http://soffr.miximages.com/c%23/oSqCa.png

e- Final – lance le projet et arrête de voir la nouvelle boîte de saisie automatique dans ToolBox.

Maintenant, vous pouvez append les deux méthodes suivantes que vous pouvez utiliser avec SQL

1- dans 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 myList = new List(); foreach (DataRow row in dt.Rows) { myList.Add((ssortingng)row[0]); } autoCompleteTextbox1.AutoCompleteList = myList; } 

2- dans l’événement 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 myList = new List(); foreach (DataRow row in dt.Rows) { myList.Add((ssortingng)row[0]); } autoCompleteTextbox2.AutoCompleteList = myList; }