it-swarm.com.de

Wie kann ich dynamisch Auto-Vervollständigen-Einträge in einer C # combobox oder Textbox ändern?

Ich habe ein Kombinationsfeld in C # und möchte Vorschläge für die automatische Vervollständigung verwenden. Ich möchte jedoch in der Lage sein, die Einträge für die automatische Vervollständigung als Benutzertypen zu ändern, da die möglichen gültigen Einträge viel zu zahlreich sind, um beim Start den AutoCompleteStringCollectionauszufüllen.

Angenommen, ich lasse den Benutzer einen Namen eingeben. Ich habe eine Liste möglicher Vornamen ("Joe", "John") und eine Liste möglicher Nachnamen ("Bloggs", "Smith"), aber wenn ich jeweils tausend habe, dann wären das eine Million mögliche Zeichenfolgen - Zu viele, um die Einträge für die automatische Vervollständigung einzugeben. Zunächst möchte ich nur die Vornamen als Vorschläge haben ("Joe", "John"), und nachdem der Benutzer den Vornamen ("Joe") eingegeben hat, möchte ich die vorhandenen Einträge für die automatische Vervollständigung entfernen und ersetzen sie mit einem neuen Satz bestehend aus dem gewählten Vornamen, gefolgt von den möglichen Nachnamen ("Joe Bloggs", "Joe Smith"). Dazu habe ich den folgenden Code ausprobiert:

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

Dies funktioniert jedoch nicht richtig. Es scheint, dass der Aufruf von Clear () dazu führt, dass der automatische Vervollständigungsmechanismus "ausgeschaltet" wird, bis das nächste Zeichen im Kombinationsfeld angezeigt wird. Wenn jedoch das nächste Zeichen angezeigt wird, ruft der obige Code Clear () erneut auf, sodass der Benutzer es nie tut Tatsächlich sieht die automatische Vervollständigung Funktionalität. Außerdem wird der gesamte Inhalt des Kombinationsfelds ausgewählt, sodass Sie zwischen jedem Tastendruck die Auswahl des vorhandenen Texts aufheben müssen, wodurch dieser unbrauchbar wird. Wenn ich den Aufruf von Clear () entferne, funktioniert die automatische Vervollständigung, aber es scheint, dass der Aufruf von AddRange() keine Auswirkung hat, da die neuen Vorschläge, die ich hinzufüge, nicht in der Dropdown-Liste für die automatische Vervollständigung angezeigt werden.

Ich habe nach einer Lösung dafür gesucht und verschiedene Vorschläge gesehen, aber ich kann keine davon zum Laufen bringen - entweder scheint die Funktion zur automatischen Vervollständigung deaktiviert zu sein oder es werden keine neuen Zeichenfolgen angezeigt. Hier ist eine Liste von Dingen, die ich ausprobiert habe:

  • Rufen Sie BeginUpdate() auf, bevor Sie die Zeichenfolgen ändern, und EndUpdate() anschließend.
  • Aufrufen von Remove() für alle vorhandenen Zeichenfolgen anstelle von Clear ().
  • Löschen Sie den Text aus dem Kombinationsfeld, während ich die Zeichenfolgen aktualisiere, und fügen Sie ihn anschließend wieder hinzu.
  • Setzen Sie AutoCompleteModeauf "None", während ich die Zeichenfolgen ändere, und setzen Sie es anschließend wieder auf "SuggestAppend".
  • Verknüpfen des Ereignisses TextUpdateoder KeyPressanstelle von TextChangedname__.
  • Ersetzen Sie den vorhandenen AutoCompleteCustomSourcenamen__ jedes Mal durch einen neuen AutoCompleteStringCollectionnamen__.

Keines davon hat geholfen, auch in verschiedenen Kombinationen. Spence schlug vor, die Funktion ComboBoxzu überschreiben, mit der die Liste der Zeichenfolgen abgerufen wird, die bei der automatischen Vervollständigung verwendet werden sollen. Unter Verwendung eines Reflektors habe ich in der Klasse ComboBoxeinige Methoden gefunden, die vielversprechend aussehen - GetStringsForAutoComplete() und SetAutoComplete(), aber beide sind privat, sodass ich nicht über eine abgeleitete Klasse auf sie zugreifen kann. Ich konnte das nicht weiter verfolgen.

Ich habe versucht, ComboBoxdurch TextBoxzu ersetzen, da die Schnittstelle für die automatische Vervollständigung identisch ist und ich festgestellt habe, dass sich das Verhalten geringfügig unterscheidet. Mit dem TextBoxscheint es besser zu funktionieren, da der Anfügeteil der automatischen Vervollständigung ordnungsgemäß funktioniert, der Suggest-Teil jedoch nicht - das Vorschlagsfeld blinkt kurz auf, verschwindet dann jedoch sofort.

Also dachte ich "Okay, ich lebe ohne die Suggest-Funktionalität und benutze stattdessen einfach Anhängen". Wenn ich jedoch AutoCompleteModeauf Anhängen setze, erhalte ich eine Zugriffsverletzungsausnahme. Dasselbe passiert mit Suggest - der einzige Modus, der keine Ausnahmen auslöst, ist SuggestAppendname__, obwohl sich der Suggest-Teil dann nicht richtig verhält.

Ich dachte, dass es unmöglich sein sollte, Ausnahmen für Zugriffsverletzungen zu erhalten, wenn mit C # verwalteter Code verwendet wird. Avram schlug vor, dass ich "lock" verwende, um dies zu beheben, aber ich weiß nicht, was ich sperren soll - das einzige, was ein SyncRoot-Mitglied hat, ist AutoCompleteStringCollectionund das Sperren, das die Ausnahmen für Zugriffsverletzungen nicht verhindert. Ich habe auch versucht, ComboBoxoder TextBoxzu sperren, aber das hat auch nicht geholfen. So wie ich es verstehe, verhindert die Sperre nur andere Sperren. Wenn der zugrunde liegende Code die Sperre nicht verwendet, macht meine Verwendung keinen Unterschied.

Das Fazit ist, dass ich derzeit keine TextBoxoder ComboBoxmit dynamischer Auto-Vervollständigung verwenden kann. Hat jemand einen Einblick, wie ich das erreichen könnte?

Aktualisieren:

Ich habe das noch nicht zum Laufen gebracht, aber ich habe mehr herausgefunden. Vielleicht inspiriert ein Teil davon einen anderen, eine Lösung zu finden.

Ich habe versucht, ComboBoxdurch TextBoxzu ersetzen, da die Schnittstelle für die automatische Vervollständigung identisch ist und ich festgestellt habe, dass sich das Verhalten geringfügig unterscheidet. Mit dem TextBoxscheint es besser zu funktionieren, da der Anfügeteil der automatischen Vervollständigung ordnungsgemäß funktioniert, der Suggest-Teil jedoch nicht - das Vorschlagsfeld blinkt kurz auf, verschwindet dann jedoch sofort.

Also dachte ich: "Okay, ich lebe ohne die Suggest-Funktionalität und benutze stattdessen einfach Anhängen." Wenn ich jedoch AutoCompleteModeauf Anhängen setze, erhalte ich eine Zugriffsverletzungsausnahme. Dasselbe passiert mit Suggest - der einzige Modus, der keine Ausnahmen auslöst, ist SuggestAppendname__, obwohl sich der Suggest-Teil dann nicht richtig verhält.

Ich dachte, dass es unmöglich sein sollte, Ausnahmen für Zugriffsverletzungen zu erhalten, wenn C # -verwalteter Code verwendet wird, aber das Fazit ist, dass ich derzeit keinen TextBoxoder ComboBoxmit irgendeiner Art dynamischer Auto-Vervollständigung verwenden kann. Hat jemand einen Einblick, wie ich das erreichen könnte?

Update 2:

Nachdem ich verschiedene andere Dinge ausprobiert hatte, wie das Ändern der automatischen Vervollständigung in einem Arbeitsthread und die Verwendung von BeginInvoke() zur Simulation des PostMessage () - Verhaltens, gab ich schließlich auf und implementierte einfach mein eigenes Dropdown-Menü für die automatische Vervollständigung mithilfe eines Listenfelds. Es ist viel reaktionsschneller als das eingebaute, und ich habe weniger Zeit damit verbracht, es zum Laufen zu bringen, als ich es versucht habe. Die Lektion für alle anderen, die dieses Verhalten wollen, lautet also: Sie sind wahrscheinlich besser dran Implementieren Sie es selbst.

46
Sam Hopkins

Ich hatte das gleiche Problem und fand eine extrem einfache Problemumgehung. Wie alle anderen hier konnte ich kein Mittel finden, um das Verhalten der Komponente zu kontrollieren, also musste ich es akzeptieren.

Das natürliche Verhalten ist: Sie können die Liste nicht jedes Mal dynamisch füllen, wenn der Benutzer das Textfeld eingibt. Sie müssen es einmal ausfüllen, und dann übernimmt der AutoComplete-Mechanismus die Kontrolle. Die Schlussfolgerung lautet: Sie sollten die AutoCompleteCustomSource mit allen möglichen Einträgen in Ihrer Datenbank füllen, damit sie wie gewünscht funktioniert.

Natürlich ist dies nicht möglich, wenn Sie Millionen von Datensätzen haben, um die Liste aufzufüllen. Leistungsprobleme bei der Datenübertragung und der AutoComplete-Mechanismus selbst ermöglichen dies nicht.

Die Kompromisslösung, die ich gefunden habe, lautete: Dynamisches Auffüllen des AutoCompleteCustomSource-Objekts, wenn die Textlänge genau N Zeichen (in meinem Fall 3) erreicht. Dies funktionierte, weil die Komplexität drastisch reduziert wurde. Die Anzahl der Datensätze, die aus der Datenbank abgerufen werden und die diesen drei anfänglichen Zeichen entsprechen, war klein genug, um Leistungsprobleme zu vermeiden.

Der Hauptnachteil ist: Benutzern wird die AutoVervollständigungsliste erst angezeigt, wenn sie das n-te Zeichen eingeben. Es scheint jedoch, als würden Benutzer nicht wirklich eine sinnvolle AutoComplete-Liste erwarten, bevor 3 Zeichen eingegeben werden.

Hoffe das hilft.

13
Alexandre Mafra

Das hat für mich funktioniert, Sie addRange nicht auf dieselbe AutoCompleteStringCollection, sondern erstellen immer eine neue.

form.fileComboBox.TextChanged += (sender, e) => {
    var autoComplete = new AutoCompleteStringCollection();
    string[] items = CustomUtil.GetFileNames();
    autoComplete.AddRange(items);
    form.fileComboBox.AutoCompleteCustomSource = autoComplete;
};
2
Jaanus

Ich habe das nicht getestet, aber es lohnt sich vielleicht.

Doppelklicken Sie anstelle des AutoCompleteCustomSource-Puffers, indem Sie zwei Instanzen beibehalten. Wenn sich der Text ändert, rufen Sie GetNameSuggestions () auf, erstellen Sie die Zeichenfolgen für die derzeit nicht verwendete und setzen Sie dann ComboName.AutoCompleteCustomSource auf die gerade eingerichtete.

Ich denke, es sollte ungefähr so ​​aussehen.

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
Adam Haile

Ich denke, Sie möchten vielleicht den Reflektor herausnehmen und das Autocomplete-Verhalten in der Combobox selbst außer Kraft setzen. Ich bin sicher, dass die Autovervollständigung eine Funktion aufrufen würde, die auf die Autovervollständigungsliste zugreift. Wenn Sie diese Funktion finden und überschreiben können, können Sie jedes gewünschte Verhalten verwenden.

Sehen Sie, welche Dokumentation Sie in der Combobox-Klasse selbst finden.

1
Spence

Nachdem ich alle hier angebotenen Lösungen ausprobiert hatte (ohne Erfolg), habe ich etwas gefunden, das für mich funktioniert:

private void CellBox_TextChanged(object sender, EventArgs e)
{
    ((TextBox)sender).TextChanged -= CellBox_TextChanged;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteMode = AutoCompleteMode.None;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteCustomSource = null;                
    aCSC.Clear();
    foreach (string value in Autocompletevalues())
    {
        aCSC.Add(value);
    }
    ((TextBox)dataGridView1.EditingControl).AutoCompleteCustomSource = aCSC;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteMode = AutoCompleteMode.Suggest;
    ((TextBox)sender).TextChanged += CellBox_TextChanged;
}

Schritte:

  • Eventhandler deaktivieren
  • Deaktivieren Sie den Autovervollständigungsmodus
  • Setzen Sie Source auf null
  • AutoCompleteStringCollection (aCSC) aktualisieren
  • Setzen Sie Source auf aktualisiertes AutoCompleteStringCollection
  • Aktivieren Sie den Autovervollständigungsmodus
  • eventhandler aktivieren

Ich hoffe es hilft jemandem ..

0
steloe

Sam, hast du das herausgefunden? Ich bin in die gleiche Situation geraten. Clear () scheint die Ausnahme zu verursachen. Ich habe den Anruf entfernt, um zu löschen, und ich bekomme die richtigen Vorschläge, obwohl die Sammlung ständig wächst.

Auch bezüglich der privaten Mitglieder: Sie können über Reflection darauf zugreifen:

PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });
0
fixitchris

Dies ist ein sehr altes Problem, das ich kenne, aber es ist eines, das heute noch existiert ... Meine Problemumgehung bestand darin, den Autocomplete-Modus und die Quelleigenschaften auf "none" zu setzen und die Elemente im KeyUp-Ereignis manuell zu aktualisieren.

Ich bin mir sicher, dass es hackig ist, aber es funktioniert für eine ganze Weile ohne Probleme, unabhängig von der Geschwindigkeit, mit der die Daten eingegeben werden, und der zusätzliche Bonus für mein Haar wächst nach.

Sie können auch auswählen, ob Sie nur vorschlagen oder vorschlagen und anhängen sollen. Ich hoffe, es kann jemandem helfen.

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
TEDSON

update: Hauptgrund für die Sperrung dieser Stelle ist

es funktioniert :) die meisten "geheimnisvollen ausnahmen", die ich je habe, nach diesem trick verschwinden


  1. die Sperre wie in diesem Code kann bei Ihrer Ausnahme helfen
  2. wie bereits erwähnt, gibt es weniger Probleme bei der Verwendung von Textfeldern
  3. in diesem Code funktioniert SuggestAppend einwandfrei


    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
Avram

Für mich war das Geheimnis das Ereignis TextChanged und kein KeyDown/Up/Press usw.

Update: Nachdem ich andere Probleme mit der dynamischen Änderung von AutoCompleteCustomSource hatte, gab ich die eingebaute Autocomplete-Funktion auf und implementierte meine eigene in viel kürzerer Zeit, als ich sie ursprünglich verschwendet hatte. In nicht verwaltetem Code scheint es einige Probleme zu geben, die das ComboBox-Steuerelement implementieren. Insbesondere hatte ich Probleme mit dem Auslösen des TextChanged-Ereignishandlers, wenn er sollte. Ich entschied mich, nur die OnKeyDown/Press/Up-Handler in meiner benutzerdefinierten Implementierung zu verwenden, und das schien zuverlässiger zu sein.

0
Boog
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
0
Randy

Versuchen Sie es nicht, aber für Ihren speziellen Fall könnten Sie Folgendes schreiben:

    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
Black Shell

Ich war hier zunächst auf der Suche nach einer Lösung, habe aber jetzt meine eigene gefunden. 

Der Trick besteht darin, Clear () nicht für die AutoCompleteCustomSource aufzurufen, sondern alle Elemente in einer for-Schleife zu entfernen und die Liste mit den neuen Daten neu zu erstellen. In meinem Fall (einer Buchsammlungsanwendung) rufe ich Autorennamen aus einer Datenbank mit einem bestimmten Anfangsbuchstaben ab, anstatt das ganze Los. Beachten Sie, dass dies nur funktioniert, wenn das Textfeld der Combobox leer ist oder ist. 

    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
Jim Cramer

Die beste Lösung hierfür ist die Verwendung der Event-Handler von combobox. Mit textUpdate KeyDown DropDown und ChangeCommit können Sie den Autocomplet-Modus imitieren und anpassen, was gesucht werden soll und was in der Dropdown-Liste erscheinen soll.

Ich fand this answer nützlich, aber es ist in Visual C++ codiert und es ist toolstripcombobox, aber das Konzept ist identisch. Wie auch immer, es gibt eine große Ähnlichkeit von c # und c ++ in .net und es sollte kein Problem sein, um die Lösung zu verstehen.

Angepasste Autosuche von ToolStripCombobox in Visual C++

0
catzilla