43 votes

C # AutoComplete

Je suis en train d'ajouter une fonctionnalité saisie semi-automatique d'une zone de texte, les résultats sont à venir à partir d'une base de données. Ils viennent dans le format de

[001], Prénom Milieu

Actuellement, vous devez taper [001]... pour obtenir les entrées à afficher. Donc, le problème, c'est que je le veux, même si j'ai taper le prénom de la première. Donc, si une entrée a été

[001] Smith, John D

si j'ai commencé à taper John puis cette entrée doit s'afficher dans les résultats de l'auto complète.

Actuellement, le code ressemble à quelque chose comme

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());
    }
}

résultats est un jeu de données contenant les résultats de la requête

La requête est une recherche simple requête à l'aide de l'instruction like. Le bon les résultats sont renvoyés si nous n'avons pas utiliser la saisie semi-automatique et juste jeter les résultats dans un tableau.

Tous les conseils?

EDIT:

Voici la requête qui renvoie les résultats

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

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

50voto

Steven Richards Points 2291

L'existant, la fonctionnalité de saisie semi-automatique prend uniquement en charge la recherche par préfixe. Il ne semble pas être une manière décente pour remplacer le comportement.

Certaines personnes ont mis en œuvre leurs propres fonctions de saisie semi-automatique en substituant l' OnTextChanged événement. C'est probablement votre meilleur pari.

Par exemple, vous pouvez ajouter un ListBox juste en dessous de l' TextBox et de définir sa visibilité par défaut à false. Ensuite, vous pouvez utiliser l' OnTextChanged cas de l' TextBox et de la SelectedIndexChanged cas de l' ListBox pour afficher et sélectionner des éléments.

Cela semble fonctionner assez bien que rudimentaire exemple:

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

Il y a beaucoup plus que vous pourriez faire sans trop d'effort: ajouter du texte à la zone de texte, saisir de nouvelles commandes de clavier, et ainsi de suite.

6voto

Jim Scott Points 1139

Si vous décidez d'utiliser une requête basée sur les entrées de l'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);
 

4voto

noelicus Points 3788

Voici une implémentation qui hérite simplement de la classe de contrôle ComboBox . S'il vous plaît utilisez-le, modifiez-le et modifiez la réponse si vous l'améliorez!

 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.ContainsNoCase(SearchText))
                    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!
                        listBoxChild.SelectedIndex = IntEx.Limit(NewIx, 0, listBoxChild.Items.Count - 1);
                        return true;

                    case 0x0D: // return (use the currently selected item)
                        CopySelection();
                        return true;
                }
            }
        }

        return false;
    }
}
 

0voto

Damovisa Points 6756

Si vous exécutez cette requête (avec {0} d'être remplacé par la chaîne entrée), vous pourriez avoir besoin:

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

LIKE a toujours besoin de l' % caractère... Et oui, vous devez utiliser des paramètres plutôt que de faire confiance à l'entrée de l'utilisateur :)

Aussi, vous semblent être de retour l' Name colonne, mais l'interrogation sur l' Details colonne. Donc, si quelqu'un tape dans "John Smith", si ce n'est pas dans l' Details colonne que vous n'obtenez pas ce que vous voulez de retour.

0voto

eldoraman Points 1

essayez ce code ...

         textBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
        textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
        AutoCompleteStringCollection DataCollection = new AutoCompleteStringCollection();
        getData(DataCollection);
        textBox1.AutoCompleteCustomSource = DataCollection
 

connexion db ..

     private void getData(AutoCompleteStringCollection dataCollection)
    {
        string connetionString = null;
        SqlConnection connection ;

        SqlCommand command ;
        SqlDataAdapter adapter = new SqlDataAdapter();
        DataSet ds = new DataSet();
        connetionString = "Data Source=.;Initial Catalog=pubs;User ID=sa;password=zen412";
        string sql = "SELECT DISTINCT [fname] FROM [employee]";
        connection = new SqlConnection(connetionString);
        try
        {
            connection.Open();
            command = new SqlCommand(sql, connection);
            adapter.SelectCommand = command;
            adapter.Fill(ds);
            adapter.Dispose();
            command.Dispose();
            connection.Close();
            foreach (DataRow row in ds.Tables[0].Rows)
            {
                dataCollection.Add(row[0].ToString());
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Can not open connection ! ");
        }
    }
 

Source complète ... C # Zone de saisie semi-automatique à partir de valeurs de base de données

Eldo

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X