Come posso modificare dynamicmente le voci di completamento automatico in una combobox o una casella di testo C #?

Ho una casella combinata in C # e desidero utilizzare i suggerimenti di completamento automatico con esso, tuttavia desidero essere in grado di modificare le voci di completamento automatico quando l’utente digita, perché le voci valide possibili sono troppo numerose per popolare AutoCompleteStringCollection all’avvio.

Ad esempio, supponiamo che io stia permettendo all’utente di digitare un nome. Ho una lista di possibili nomi di battesimo (“Joe”, “John”) e una lista di cognomi (“Bloggs”, “Smith”), ma se ne ho un migliaio ciascuno, allora sarebbe un milione di possibili stringhe – troppi per inserire le voci di completamento automatico. Quindi inizialmente voglio avere solo i primi nomi come suggerimenti (“Joe”, “John”), e poi, una volta che l’utente ha digitato il nome, (“Joe”), voglio rimuovere le voci di completamento automatico esistenti e sostituire con un nuovo set composto dal nome scelto seguito dai cognomi possibili (“Joe Bloggs”, “Joe Smith”). Per fare ciò, ho provato il seguente codice:

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

Tuttavia, questo non funziona correttamente. Sembra che la chiamata a Clear () faccia sì che il meccanismo di completamento automatico si “spenga” finché il carattere successivo non compare nella casella combinata, ma ovviamente quando il carattere successivo appare di nuovo il codice sopra Chiama (), quindi l’utente non effettivamente vede la funzionalità di completamento automatico. Consente inoltre di selezionare l’intero contenuto della casella combinata, quindi tra ogni pressione di un tasto è necessario deselezionare il testo esistente, il che lo rende inutilizzabile. Se rimuovo la chiamata a Clear (), il completamento automatico funziona, ma sembra che la chiamata a AddRange() non abbia alcun effetto, perché i nuovi suggerimenti che aggiungo non compaiono nel menu a tendina automatico completo.

Ho cercato una soluzione a questo e ho visto varie cose suggerite, ma non riesco a far funzionare nessuno di loro: la funzionalità di completamento automatico appare disabilitata o le nuove stringhe non vengono visualizzate. Ecco una lista di cose che ho provato:

  • Chiamando BeginUpdate() prima di modificare le stringhe e EndUpdate() seguito.
  • Chiamando Remove() su tutte le stringhe esistenti invece di Clear ().
  • Cancellando il testo dalla casella combinata mentre aggiorno le stringhe e aggiungendole di nuovo in seguito.
  • Impostazione di AutoCompleteMode su “None” mentre cambio le stringhe e successivamente su “SuggestAppend”.
  • TextUpdate evento TextUpdate o KeyPress invece di TextChanged .
  • Sostituzione di AutoCompleteCustomSource con una nuova AutoCompleteStringCollection esistente ogni volta.

Nessuno di questi ha aiutato, anche in varie combinazioni. Spence mi ha suggerito di provare a sovrascrivere la funzione ComboBox che ottiene l’elenco delle stringhe da utilizzare in completamento automatico. Usando un riflettore ho trovato un paio di metodi nella class ComboBox che sembrano promettenti – GetStringsForAutoComplete() e SetAutoComplete() , ma sono entrambi privati ​​quindi non posso accedervi da una class derivata. Non potrei continuare oltre.

Ho provato a sostituire il ComboBox con un TextBox , perché l’interfaccia di completamento automatico è la stessa, e ho scoperto che il comportamento è leggermente diverso. Con il TextBox sembra funzionare meglio, in quanto la parte Append del completamento automatico funziona correttamente, ma la parte Suggest non lo fa – la finestra dei suggerimenti lampeggia brevemente alla vita ma poi scompare immediatamente.

Quindi ho pensato “Ok, vivrò senza la funzionalità Suggest e useremo invece Append”, tuttavia quando imposto l’ AutoCompleteMode su Append, ottengo un’eccezione di violazione di accesso. La stessa cosa accade con Suggest: l’unica modalità che non genera eccezioni è SuggestAppend , anche se la parte Suggest non funziona correttamente.

Ho pensato che fosse imansible ottenere le eccezioni di violazione di accesso quando si utilizza il codice gestito da C #. Avram mi ha suggerito di utilizzare “lock” per risolvere il problema, ma non so cosa dovrei bloccare: l’unica cosa che ha un membro SyncRoot è AutoCompleteStringCollection e il blocco che non impedisce le eccezioni di violazione di accesso. Ho anche provato a bloccare il ComboBox o il TextBox , ma non è stato di alcun aiuto. A quanto ho capito, il blocco impedisce solo altri blocchi, quindi se il codice sottostante non sta utilizzando il blocco, il mio utilizzo non farà alcuna differenza.

Il risultato di tutto ciò è che attualmente non posso usare un TextBox o un ComboBox con il completamento automatico dinamico. Qualcuno ha qualche idea su come potrei ottenere questo?

Aggiornare:

Non ho ancora funzionato, ma ho scoperto qualcosa di più. Forse parte di questo ispirerà qualcun altro a trovare una soluzione.

Ho provato a sostituire il ComboBox con un TextBox , perché l’interfaccia di completamento automatico è la stessa, e ho scoperto che il comportamento è leggermente diverso. Con il TextBox sembra funzionare meglio, in quanto la parte Append del completamento automatico funziona correttamente, ma la parte Suggest non lo fa – la finestra dei suggerimenti lampeggia brevemente alla vita ma poi scompare immediatamente.

Quindi ho pensato “Ok, vivrò senza la funzionalità Suggest e useremo invece Append”, tuttavia quando imposto l’ AutoCompleteMode su Append, ottengo un’eccezione di violazione di accesso. La stessa cosa accade con Suggest: l’unica modalità che non genera eccezioni è SuggestAppend , anche se la parte Suggest non funziona correttamente.

Ho pensato che fosse imansible ottenere le eccezioni di violazione di accesso quando si utilizza il codice gestito da C #, ma in ogni caso, il risultato è che attualmente non posso utilizzare un TextBox o un ComboBox con alcun tipo di completamento automatico dinamico. Qualcuno ha qualche idea su come potrei ottenere questo?

Aggiornamento 2:

Dopo aver provato varie altre cose, come la modifica del completamento automatico in un thread di lavoro e l’utilizzo di BeginInvoke() per simulare il comportamento del tipo PostMessage (), alla fine ho rinunciato e ho implementato il mio menu a discesa completo automatico utilizzando una casella di riepilogo. È molto più reattivo di quello integrato e ho impiegato meno tempo a farlo, rispetto a quello che cercavo per far funzionare quello integrato, quindi la lezione per chiunque altro voglia questo comportamento è – probabilmente stai meglio implementandolo da solo.

Ho avuto lo stesso problema e ho trovato una soluzione estremamente semplice. Come tutti gli altri qui, non sono riuscito a trovare alcun mezzo per controllare il comportamento del componente, quindi ho dovuto accettarlo.

Il comportamento naturale è: non è ansible popolare in modo dinamico l’elenco ogni volta che l’utente digita nella casella di testo. Devi popolarlo una volta e poi il meccanismo di Completamento automatico prende il controllo. La conclusione è: dovresti compilare AutoCompleteCustomSource con ogni voce ansible nel tuo database per farlo funzionare come vogliamo.

Ovviamente questo non è fattibile se si dispone di milioni di record per popolare l’elenco. Problemi di prestazioni nel trasferimento dei dati e il meccanismo di completamento automatico stesso non ti consentiranno di farlo.

La soluzione di compromesso che ho trovato era: popola in modo dinamico AutoCompleteCustomSource ogni volta che la lunghezza del testo raggiunge esattamente N caratteri (3 nel mio caso). Questo ha funzionato perché la complessità è stata drasticamente ridotta. Il numero di record recuperati dal database che corrispondono a questi 3 caratteri iniziali era abbastanza piccolo da evitare problemi di prestazioni.

Lo svantaggio principale è: gli utenti non saranno presentati nell’elenco Completamento automatico finché non digitano il carattere N-esimo. Ma sembra che gli utenti non si aspettino davvero un elenco di completamento automatico significativo prima di digitare 3 caratteri.

Spero che questo ti aiuti.

Penso che potresti voler estrarre il riflettore e guardare a sovrascrivere il comportamento di autocompletamento nella casella combinata stessa. Sono certo che il completamento automatico chiamerebbe una funzione che accede all’elenco di autocompletamento. Se riesci a trovare questa funzione e a sovrascriverla, puoi utilizzare qualsiasi comportamento tu voglia.

Guarda quale documentazione puoi trovare sulla class della combobox stessa.

Non ho provato questo, ma potrebbe valerne la pena.

Invece di cancellare AutoCompleteCustomSource, raddoppiare il buffer mantenendo due istanze. Quando il testo cambia, chiama GetNameSuggestions () e crea le stringhe per quello attualmente non in uso, quindi imposta ComboName.AutoCompleteCustomSource su quello appena impostato.

Penso che dovrebbe assomigliare a questo.

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

Questo ha funzionato per me, non addRange allo stesso addRange , ma piuttosto crearne uno nuovo ogni volta.

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

aggiornamento: motivo principale per mettere il blocco su questo posto è

il suo funzionamento 🙂 la maggior parte di “eccezione misteriosa” che io abbia mai, dopo questo trucco scomparire


  1. la serratura come in questo codice, può aiutare con la tua eccezione
  2. come hai detto prima, c’è meno problema nell’usare la casella di testo
  3. in questo codice, SuggestAppend funziona bene

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

Sam, hai capito? Sto correndo nella stessa situazione. Clear () sembra causare l’eccezione. Ho rimosso la chiamata per cancellare e sto ricevendo l’evento dei suggerimenti corretto anche se la raccolta continua a crescere …

Inoltre, per quanto riguarda i membri privati: puoi accedervi utilizzando la reflection:

 PropertyInfo[] props = [object].GetType().GetProperties({flags go here}); props[0].SetValue(this, new object[] { 0 }); 
 if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text)) textBox3.AutoCompleteCustomSource.Add(textBox3.Text); 

Sono venuto qui inizialmente cercando una soluzione, ma ora ho trovato il mio.

Il trucco non è chiamare Clear () su AutoCompleteCustomSource ma rimuovere tutti gli elementi in un ciclo for e quindi ribuild l’elenco con i nuovi dati. Nel mio caso (un’applicazione per la raccolta di libri) sto recuperando i nomi degli autori da un database con una lettera iniziale specifica, invece dell’intero lotto. Si noti che questo funzionerà solo se la parte della casella di testo della casella combinata è o è diventata vuota.

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

Non provarlo, ma per il tuo caso specifico potresti codificare qualcosa come:

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

Per me il segreto era usare l’evento TextChanged e nessuno dei KeyDown / Up / Press, ecc.

Aggiornamento: dopo aver riscontrato altri problemi con la modifica dynamic di AutoCompleteCustomSource, alla fine ho abbandonato la funzionalità di completamento automatico integrata e implementato la mia in un tempo molto più breve rispetto a quello che avevo sprecato in origine. Sembra che ci siano alcuni problemi nel codice non gestito che implementa il controllo ComboBox. In particolare, stavo avendo problemi con il gestore di eventi TextChanged che sparava quando doveva. Ho deciso di utilizzare solo i gestori OnKeyDown / Press / Up nella mia implementazione personalizzata e ciò sembrava essere più affidabile.

La soluzione migliore per questo è utilizzare i gestori di eventi di combobox. Usando textUpdate KeyDown DropDown e ChangeCommit , puoi imitare autocompletemode e puoi personalizzare cosa cercare e cosa apparire nel menu a tendina.

Ho trovato questa risposta utile ma è codificata in visual c ++ ed è toolstripcombobox ma il concetto è identico . Comunque c’è un’enorme somiglianza tra c # e c ++ in .net e non dovrebbe essere un problema nel capire la soluzione.

Autosearch personalizzato di ToolStripCombobox in Visual C ++

Questo è un problema molto vecchio, lo so, ma è uno che esiste ancora oggi. La mia soluzione alternativa era impostare la modalità di completamento automatico e le proprietà di origine su “nessuno” e aggiornare manualmente gli elementi sull’evento KeyUp.

Sono sicuro che sia hacky ma funziona perfettamente per me senza problemi per un po ‘, indipendentemente dalla velocità con cui i dati vengono inseriti, con il bonus in più dei miei capelli che iniziano a ricrescere.

Puoi anche scegliere se suggerire o suggerire e aggiungere. Spero possa aiutare qualcuno.

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

usa questo codice

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