Comment puis-je changer dynamiquement des entrées automatiques dans un c# combobox ou une boîte de texte?

j'ai un combobox en C# et je veux utiliser des suggestions auto complètes avec elle, cependant je veux être en mesure de changer les entrées auto complètes comme les types d'utilisateur, parce que les entrées valides possibles sont beaucoup trop nombreux pour remplir le AutoCompleteStringCollection au démarrage.

par exemple, supposons que je laisse l'utilisateur taper un nom. J'ai une liste de prénoms possibles ("Joe", "John") et une liste de noms ("Dupont", "Smith"), mais si j'ai un millier de chaque, puis ce serait un million de chaînes possibles - trop nombreuses à mettre dans les entrées automatiques complètes. Donc, au départ, je veux avoir juste les prénoms comme suggestions ("Joe", "John") , et puis une fois que l'Utilisateur a tapé le prénom, ("Joe"), je veux supprimer les entrées automatiques existantes et les remplacer par un nouvel ensemble composé du prénom choisi suivi des noms de famille possibles ("Joe Bloggs", "Joe Smith"). Pour ce faire, j'ai essayé le code suivant:

void InitializeComboBox()
{
    ComboName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    ComboName.AutoCompleteSource = AutoCompleteSource.CustomSource;
    ComboName.AutoCompleteCustomSource = new AutoCompleteStringCollection();
    ComboName.TextChanged += new EventHandler( ComboName_TextChanged );
}

void ComboName_TextChanged( object sender, EventArgs e )
{
    string text = this.ComboName.Text;
    string[] suggestions = GetNameSuggestions( text );

    this.ComboQuery.AutoCompleteCustomSource.Clear();
    this.ComboQuery.AutoCompleteCustomSource.AddRange( suggestions );
}

cependant, cela ne fonctionne pas correctement. Il semble que l'appel à Clear() provoque le Mécanisme Automatique complet de "désactiver" jusqu'à ce que le caractère suivant apparaisse dans la boîte bascule, mais bien sûr quand le caractère suivant apparaît les appels de code ci-dessus Clear () à nouveau, de sorte que l'utilisateur ne voit jamais réellement la fonctionnalité automatique complète. Il provoque également le contenu entier de la boîte combo pour être sélectionné, donc entre chaque keypress vous devez désélectionner le texte existant, ce qui le rend inutilisable. Si je supprime l'appel à Clear() alors l'auto complète des oeuvres, mais il semble que le AddRange() appel n'a aucun effet, parce que les nouvelles suggestions que j'ajoute n'apparaissent pas dans l'auto complet déroulant.

j'ai cherché une solution à ce problème, et j'ai vu diverses choses suggérées, mais je ne peux pas les faire fonctionner - soit la fonctionnalité automatique complète apparaît désactivée, ou de nouvelles chaînes n'apparaissent pas. Voici une liste de choses que j'ai essayées:

  • appelant BeginUpdate() avant de changer les cordes et EndUpdate() après.
  • appelant Remove() sur toutes les chaînes existantes au lieu de Clear().
  • effaçant le texte du combobox pendant que je mets à jour les chaînes, et l'ajoutant en arrière par la suite.
  • réglant le AutoCompleteMode à" None "pendant que je change les cordes, et le ramenant à" SuggestAppend " après.
  • Accrocher l'événement TextUpdate ou KeyPress au lieu de TextChanged .
  • remplaçant l'actuel AutoCompleteCustomSource par un nouveau AutoCompleteStringCollection à chaque fois.

aucun de ceux-ci n'a aidé, même dans diverses combinaisons. Spence a suggéré que j'essaie de passer outre la fonction ComboBox qui obtient la liste des chaînes à utiliser dans auto complete. En utilisant un réflecteur j'ai trouvé quelques méthodes dans la classe ComboBox cela semble prometteur - GetStringsForAutoComplete() et SetAutoComplete() , mais ils sont tous les deux privés, donc je ne peux pas y accéder à partir d'une classe dérivée. Je ne pouvais pas prendre plus.

j'ai essayé de remplacer le ComboBox par un TextBox , parce que l'interface Auto complète est la même, et j'ai trouvé que le comportement est légèrement différent. Avec le TextBox il semble fonctionner mieux, en ce que la partie annexe de l'auto complète fonctionne correctement, mais la partie Suggest ne fonctionne pas - la boîte à suggestions clignote brièvement à la vie, mais disparaît immédiatement.

donc j'ai pensé" OK, je vais vivre sans la fonctionnalité Suggest et juste utiliser Append à la place", cependant quand je mets le AutoCompleteMode pour ajouter, j'obtiens une exception de violation d'accès. La même chose se produit avec Suggest - le seul mode qui ne lance pas d'exceptions est SuggestAppend , même si la partie Suggest ne se comporte pas correctement.

je pensais que c'était supposée être impossible d'obtenir des exceptions de violation d'accès en utilisant le code C # managé. Avram a suggéré que j'utilise "lock" pour corriger cela, mais je ne sais pas ce que je devrais verrouiller - la seule chose qui a un membre SyncRoot est le AutoCompleteStringCollection , et le verrouillage qui n'empêche pas les exceptions de violation de l'accès. J'ai aussi essayé de verrouiller le ComboBox ou TextBox , mais cela n'a pas aidé non plus. Comme je l'ai compris, serrure empêche seulement d'autres serrures, donc si le code sous-jacent n'utilise pas la serrure alors mon utilisation ne fera aucune différence.

le résultat de tout cela est que je ne peux pas actuellement utiliser un TextBox ou un ComboBox avec dynamique auto complète. Quelqu'un aurait-il des idées sur comment je pourrais faire?

mise à jour:

Je n'ai toujours pas obtenu ce travail, mais j'ai découvert un peu plus. Peut-être que cela incitera quelqu'un d'autre à trouver une solution.

j'ai essayé de remplacer le ComboBox par un TextBox , parce que l'interface Auto complète est la même, et j'ai trouvé que le comportement est légèrement différent. Avec le TextBox il semble fonctionner mieux, en ce que la partie annexe de l'auto complète fonctionne correctement, mais la partie suggérer Ne - la boîte de suggestion clignote brièvement à la vie, mais disparaît immédiatement.

donc j'ai pensé " OK, je vais vivre sans la fonctionnalité suggérée et juste utilisez Append à la place, "cependant quand je mets le AutoCompleteMode pour ajouter, j'obtiens une exception de violation d'accès. La même chose se produit avec Suggest - le seul mode qui ne lance pas d'exceptions est SuggestAppend , même si la partie Suggest ne se comporte pas correctement.

j'ai pensé qu'il était censé être impossible d'obtenir des exceptions d'accès en utilisant le code C # managé, mais de toute façon, le résultat est que je ne peux pas actuellement utiliser un TextBox ou un ComboBox avec n'importe quel type de dynamique automatique complet. Quelqu'un aurait-il des idées sur comment je pourrais faire?

Maj 2:

après avoir essayé plusieurs autres choses telles que changer l'autocomplete dans un worker thread, et utiliser BeginInvoke() pour simuler le comportement de type PostMessage (), j'ai finalement abandonné et j'ai juste implémenté mon propre dropdown auto complete en utilisant une boîte de liste. Il est beaucoup plus réactif que celui intégré, et j'ai passé moins de temps à le faire. que j'ai essayé de faire fonctionner le système intégré, donc la leçon pour quiconque veut ce comportement est - vous êtes probablement mieux de le mettre en œuvre vous-même.

45
demandé sur Community 2009-02-05 14:49:10

13 réponses

j'ai eu le même problème, et j'ai trouvé une solution extrêmement simple. Comme tout le monde ici, je n'ai trouvé aucun moyen de contrôler le comportement du composant, donc j'ai dû l'accepter.

le comportement naturel est: vous ne pouvez pas remplir dynamiquement la liste chaque fois que l'utilisateur tape dans la zone de texte. Vous devez le remplir une fois, puis le mécanisme AutoComplete prend le contrôle. La conclusion est: vous devez peupler L'AutoCompleteCustomSource avec chaque possibilité d'entrée dans votre base de données pour que cela fonctionne comme nous le voulons.

bien sûr, ce n'est pas viable si vous avez des millions d'enregistrements pour remplir la liste. Les problèmes de Performance dans le transfert de données et le mécanisme AutoComplete lui-même ne vous permettra pas de le faire.

la solution de compromis que j'ai trouvé était: dynamiquement peupler L'AutoCompleteCustomSource chaque fois que la longueur du texte atteint exactement n Caractères (3 dans mon cas). Cela a fonctionné parce que la complexité était considérablement réduit. Le nombre d'enregistrements tirés de la base de données qui correspondent à ces 3 caractères initiaux était suffisamment petit pour éviter tout problème de performance.

le principal inconvénient est: les utilisateurs ne seront pas présentés la liste AutoComplete jusqu'à ce qu'ils tapent le N-ème char. Mais il semble que les utilisateurs ne s'attendent pas vraiment à une liste Autocomplète significative avant que 3 caractères soient tapés.

Espérons que cette aide.

13
répondu Alexandre Mafra 2011-12-06 06:38:26

je pense que vous pourriez vouloir sortir le réflecteur et regarder la suppression du comportement autocomplete dans le combobox lui-même. Je suis certain que l'autocomplétion appellerait une fonction qui accède à la liste des autocomplètes. Si vous pouvez trouver cette fonction et de le remplacer, vous pouvez utiliser n'importe quel comportement que vous voulez.

voyez quelle documentation vous pouvez trouver sur la classe combobox elle-même.

1
répondu Spence 2009-02-05 11:51:34

Je n'ai pas testé ceci, mais ça peut valoir le coup.

au lieu de vider L'AutoCompleteCustomSource, doublez le buffer en gardant deux instances. Lorsque le texte change, appelez GetNameSuggestions () et construisez les chaînes pour celui qui n'est pas actuellement utilisé, puis définissez ComboName.AutoCompleteCustomSource à celui que vous venez de configurer.

je pense que ça devrait ressembler à ça.

AutoCompleteCustomSource accs_a;
AutoCompleteCustomSource accs_b;
bool accs_check = true; //true for accs_a, false for accs_b
void InitializeComboBox()
{
    ComboName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    ComboName.AutoCompleteSource = AutoCompleteSource.CustomSource;

    accs_a = new AutoCompleteStringCollection();
    accs_b = new AutoCompleteStringCollection();

    ComboName.AutoCompleteCustomSource = accs_a;
    ComboName.TextChanged += new EventHandler( ComboName_TextChanged );
}

void ComboName_TextChanged( object sender, EventArgs e )
{
    string text = this.ComboName.Text;

    if(accs_check)
    {
       accs_b.Clear();
       accs_b.AddRange(GetNameSuggestions( text ));
       accs_check = false;
    }
    else
    {
       accs_a.Clear();
       accs_a.AddRange(GetNameSuggestions( text ));
       accs_check = true;
    }

    this.ComboQuery.AutoCompleteCustomSource = accs_check? accs_a : accs_b;
}
1
répondu Adam Haile 2009-02-05 18:01:05

cela a fonctionné pour moi, vous ne addRange à la même AutoCompleteStringCollection , mais plutôt créer un nouveau à chaque fois.

form.fileComboBox.TextChanged += (sender, e) => {
    var autoComplete = new AutoCompleteStringCollection();
    string[] items = CustomUtil.GetFileNames();
    autoComplete.AddRange(items);
    form.fileComboBox.AutoCompleteCustomSource = autoComplete;
};
1
répondu Jaanus 2013-06-29 09:14:12

mise à jour: la raison principale pour mettre la serrure sur cet endroit est

son fonctionnement :) la plupart des "exceptions mystérieuses" que j'ai jamais eu, après ce truc disparaissent


  1. la serrure comme dans ce code, peut aider avec votre exception
  2. comme vous le mentionnez ci-dessus, il y a moins de problèmes avec l'utilisation de textbox
  3. dans ce code, SuggestAppend fonctionne bien



    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;

        textBox1.TextChanged+=new EventHandler(textBox1_TextChanged);

        col1.AddRange(new string[] { "avi avi", "avram avram" });
        col2.AddRange(new string[] { "boria boria", "boris boris" });

        textBox1.AutoCompleteCustomSource = col1;
        textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    }
    AutoCompleteStringCollection col1 = new AutoCompleteStringCollection();
    AutoCompleteStringCollection col2 = new AutoCompleteStringCollection();

    object locker = new object();
    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        lock (locker)
        {
            if (textBox1.Text.StartsWith("a") && textBox1.AutoCompleteCustomSource != col1)
            {
                textBox1.AutoCompleteCustomSource = col1;
            }
            if (textBox1.Text.StartsWith("b") && textBox1.AutoCompleteCustomSource != col2)
            {
                textBox1.AutoCompleteCustomSource = col2;
            }
        }
    }
0
répondu Avram 2009-03-16 18:47:41

Sam, as-tu trouvé une solution? Je suis en cours d'exécution dans la même situation. Clear () semble causer l'exception. J'ai supprimé l'appel à effacer et je reçois les suggestions correctes événement bien que la collection ne cesse de croître...

aussi, en ce qui concerne les membres privés: vous pouvez y accéder en utilisant la réflexion:

PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });
0
répondu fixitchris 2010-04-27 02:34:43
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
0
répondu sth 2010-04-27 02:35:35

je suis venu ici initialement à la recherche d'une solution, mais j'ai maintenant trouvé la mienne.

le truc n'est pas d'appeler Clear() sur L'AutoCompleteCustomSource mais de supprimer tous les éléments dans une boucle for et ensuite de reconstruire la liste avec les nouvelles données. Dans mon cas (une application de collection de livres) je suis en train d'extraire les noms d'auteur d'une base de données avec une lettre de départ spécifique, au lieu de tout le lot. Notez que cela ne fonctionnera que si la partie textbox du combobox est ou est devenue vide.

    private void cboAuthor_KeyDown(object sender, KeyEventArgs e)
    {
        if (cboAuthor.Text.Length == 0)
        {
            // Next two lines simple load data from the database in the
            // into a collection (var gateway), base on first letter in
            // the combobox. This is specific to my app.
            var gateway = new AuthorTableGateway();
            gateway.LoadByFirstLetter(Char.ConvertFromUtf32(e.KeyValue)[0]);

            // Clear current source without calling Clear()
            for (int i = 0; i < authorsAutoComplete.Count; i++)
                authorsAutoComplete.RemoveAt(0);

            // Rebuild with new data
            foreach (var author in gateway)
                authorsAutoComplete.Add(author.AuthorName);
        }
    }
0
répondu Jim Cramer 2010-06-18 17:10:15

N'ont pas essayé cela, mais pour votre cas spécifique vous pourriez coder quelque chose comme:

    private void txtAutoComplete_KeyUp(object sender, KeyEventArgs e)
    {

        String text = txtAutoComplete.Text;

        if (text.EndsWith(" "))
        {

            string[] suggestions = GetNameSuggestions( text ); //put [text + " "] at the begin of each array element
            txtAutoComplete.AutoCompleteCustomSource.Clear();
            txtAutoComplete.AutoCompleteCustomSource.AddRange( suggestions );

        }

    }
0
répondu Luca Fagioli 2011-02-27 15:25:56

pour moi le secret était d'utiliser L'événement TextChanged et aucun des KeyDown/up/Press, etc.

mise à jour: après avoir eu d'autres problèmes avec la modification dynamique de L'AutoCompleteCustomSource, j'ai finalement abandonné l'utilisation de la fonctionnalité autocomplete intégrée et mis en œuvre la mienne en beaucoup plus court temps que je l'avais gaspillé sur elle à l'origine. Il semble y avoir quelques problèmes dans le code non géré qui met en œuvre le contrôle ComboBox. Plus précisément, j'ai été avoir des problèmes avec le handler d'événement TextChanged qui se déclenche quand il devrait. J'ai décidé de n'utiliser que les handlers OnKeyDown/Press/Up dans mon implémentation personnalisée et cela m'a semblé plus fiable.

0
répondu Boog 2011-09-26 23:12:04

la meilleure solution est d'utiliser les gestionnaires d'événements de combobox. En utilisant Textupdate KeyDown DropDown et ChangeCommit , vous pouvez imiter autocompletemode et vous pouvez personnaliser ce qu'il faut rechercher et ce qui doit apparaître dans le drop down.

j'ai trouvé ce réponse utile, mais il est codé en visual c++ et il est toolstripcombobox mais le concept est identique . De toute façon il n'y est une énorme similarité du c# et du C++ dans .net et cela ne devrait pas être un problème pour comprendre la solution.

Personnalisé Autosearch de ToolStripCombobox dans Visual C++

0
répondu catzilla 2017-05-23 12:18:20

C'est un problème très ancien que je connais, mais qui existe encore aujourd'hui. Ma solution était de définir le mode Autocomplete et les propriétés source à 'none' et de mettre à jour manuellement les éléments sur L'événement KeyUp.

je suis sûr que c'est hacky mais il fonctionne parfaitement pour moi sans problème pendant un certain temps indépendamment de la vitesse des données est entré, avec le bonus ajouté de mes cheveux commencent à repousser.

vous pouvez également choisir de simplement suggérer, ou suggérer et annexer. J'espère que ça peut aider quelqu'un.

private void comboBox1_KeyUp(object sender, KeyEventArgs e)
    {

        if (string.IsNullOrWhiteSpace(comboBox1.Text))
        {
            e.Handled = true;
            return;
        }
        if (comboBox1.Text.Length < 3)
        {
            e.Handled = true;
            return;
        }

        if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
        {
            e.Handled = true;
            return;
        }
        else if (e.KeyCode == Keys.Back)
        {
            e.Handled = true;
            return;
        }

        string text = comboBox1.Text;

        if (e.KeyCode == Keys.Enter)
        {
            comboBox1.DroppedDown = false;
            comboBox1.SelectionStart = text.Length;
            e.Handled = true;
            return;
        }

        List<string> LS = Suggestions(comboBox1.Text);

        comboBox1.Items.Clear();
        comboBox1.Items.AddRange(LS.ToArray());

        //If you do not want to Suggest and Append
        //comment the following line to only Suggest
        comboBox1.Focus();

        comboBox1.DroppedDown = true;
        comboBox1.SelectionStart = text.Length;

        //Prevent cursor from getting hidden
        Cursor.Current = Cursors.Default;
        e.Handled = true;
    }
0
répondu TEDSON 2018-04-23 14:00:45

utilisez ce code

private void dataGridView1_EditingControlShowing(object sender,DataGridViewEditingControlShowingEventArgs e)
    {

        if (e.Control is DataGridViewComboBoxEditingControl)
        {
            ((ComboBox)e.Control).DropDownStyle = ComboBoxStyle.DropDown;
            ((ComboBox)e.Control).AutoCompleteSource = AutoCompleteSource.ListItems;
            ((ComboBox)e.Control).AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
        }

}
-2
répondu Jahanzaib Ahmad 2013-12-13 11:14:16