How can I dynamically change auto-populated entries in a C # combo box or text box?

I have a combo box in C #, and I want to use automatically complete sentences with it, however I want to be able to change automatically filled records by user type, because the allowed valid records are too numerous to fill in AutoCompleteStringCollection at startup.

As an example, suppose I allow the user to enter a name. I have a list of possible first names ("Joe", "John") and a list of last names ("Bloggs", "Smith"), but if I have a thousand, it will be a million possible lines - too many to add autocomplete. Therefore, at first I want to have only the first names as sentences ("Joe", "John"), and then, as soon as the user types the first name ("Joe"), I want to delete the existing automatically completed records and replace them with a new set, consisting of the selected first name, followed by possible surnames ("Joe Bloggs", "Joe Smith"). For this, I tried the following code:

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

However, this does not work properly. It seems that calling Clear () forces the car to “turn off” until the next character appears in the combo box, but, of course, when the next character appears, the above code will call Clear () again, so the user never actually sees the automatic full functionality. This also causes the entire contents of the combo box to be selected, so between each keystroke you must deselect the existing text, which makes it unusable. If I delete the Clear () call, then autocomplete works, but it seems that the AddRange() call has no effect, because the new offers that I add do not appear in the auto-complete drop-down list.

I searched for a solution to this and saw different things, but I can’t get them to work - either the automatic full functionality is turned off, or new lines are not displayed. Here is a list of things I've tried:

  • Call BeginUpdate() before changing the lines and EndUpdate() after.
  • Call Remove() for all existing rows instead of Clear ().
  • Removing text from the drop-down list when updating rows and adding it back.
  • Setting AutoCompleteMode to "None" when I change lines, and then back to "SuggestAppend" again.
  • Acquiring a TextUpdate or KeyPress event instead of TextChanged .
  • Each time replacing an existing AutoCompleteCustomSource with a new AutoCompleteStringCollection .

None of them helped, even in various combinations. Spence suggested that I try to override the ComboBox function, which gets a list of strings for automatic completion. Using a reflector, I found several methods in the ComboBox class that look promising - GetStringsForAutoComplete() and SetAutoComplete() , but they are both private, so I cannot access them from a derived class. I could no longer do this.

I tried replacing ComboBox with TextBox because the autocomplete interface is the same and I found that the behavior is a little different. It works better with TextBox , because the part of the Append autocomplete works correctly, but it doesn’t work in the "Suggestion" section - the suggestion window briefly flashes and then immediately disappears.

So, I thought: “Okay, I’ll live without the“ Suggest ”functionality and just use the“ Add ”), however, when I install AutoCompleteMode in the Append, I get an access violation exception. The same thing happens with the sentence - the only mode which does not throw exceptions is SuggestAppend , even though part of the sentence is not behaving correctly.

I thought that when using managed code in C # it was impossible to get access violation exceptions. Avram suggested using a “lock” to fix this, but I don’t know what I should block - the only thing the SyncRoot member has is an AutoCompleteStringCollection and a lock that does not prevent access violation exceptions. I also tried blocking a ComboBox or TextBox , but that didn't help either. As I understand it, locking only prevents other locks, so if the base code does not use locking, then my use of this will not make any difference.

The bottom line of all this is that I cannot use TextBox or ComboBox with dynamic auto-completion. Does anyone know how I can achieve this?

Update:

I still don’t have a job, but I learned something else. Perhaps some of them will inspire someone else to come up with a solution.

I tried replacing ComboBox with TextBox because the autocomplete interface is the same and I found that the behavior is a little different. It works better with TextBox , because the part of the Append autocomplete works correctly, but it doesn’t work in the "Suggestion" section - the suggestion window briefly flashes and then immediately disappears.

So, I thought, “Okay, I will live without the Suggest function and just use the Append instead,” however, when I install AutoCompleteMode in the Append, I get an access violation exception. The same thing happens with a sentence - the only mode that does not throw exceptions is SuggestAppend , even though part of the sentence does not behave correctly.

I thought that when using C # managed code, an exception was thrown due to access violation, but in any case, the result is that I can not use TextBox or ComboBox with any type of dynamic auto-completion. Does anyone know how I can achieve this?

Update 2:

Having tried various other things, such as changing autocomplete in the BeginInvoke() and using BeginInvoke() to model behavior like PostMessage (), I finally gave up and just implemented my own automatic full drop-down list using the list box. This is much more responsive than the built-in, and I spent less time on it than trying to get the built-in to work, so the lesson for everyone who wants this behavior is probably better for you to implement it yourself.

+45
c # autocomplete winforms combobox textbox
Feb 05 '09 at 11:49
source share
13 answers

I had the same problem and found a very easy workaround. Like everyone else, I could not find any means to control the behavior of the component, so I had to accept it.

Natural behavior: you cannot dynamically populate a list every time a user enters text in a text box. You need to fill it once, and then the AutoComplete mechanism gains control. Conclusion: you must populate an AutoCompleteCustomSource with every possible record in your database so that it works the way we want.

Of course, this is not viable if you have millions of entries to fill out the list. Performance problems with data transfer and the autocomplete mechanism itself will not allow you to do this.

I found a compromise solution: dynamically populate AutoCompleteCustomSource every time the text length reaches exactly N characters (3 in my case). This worked because the complexity was drastically reduced. The number of records retrieved from the database matching these 3 leading characters was small enough to avoid any performance issues.

The main disadvantage: users will not be presented with an autocomplete list until they type N-th char. But it seems that users do not expect a meaningful autocomplete list before entering three characters.

Hope this helps.

+13
Dec 06 2018-11-12T00:
source share

I think you might want to exit the reflector and see how to override the autocomplete behavior in the panel itself. I'm sure autocomplete will call a function that accesses the autocomplete list. If you can find this function and override it, you can use whatever behavior you want.

See what documentation you can find in the combobox class itself.

+1
Feb 05 '09 at 11:51
source share

I have not tested this, but it may be worth it.

Instead of clearing AutoCompleteCustomSource, double buffer, saving two instances. When the text changes, call GetNameSuggestions () and create lines for the one that is not currently in use, and then set ComboName.AutoCompleteCustomSource to the one you just configured.

I think it should look something like this.

 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
Feb 05 '09 at 18:01
source share

This worked for me, you do not addRange to the same AutoCompleteStringCollection , but rather create a new one every time.

 form.fileComboBox.TextChanged += (sender, e) => { var autoComplete = new AutoCompleteStringCollection(); string[] items = CustomUtil.GetFileNames(); autoComplete.AddRange(items); form.fileComboBox.AutoCompleteCustomSource = autoComplete; }; 
+1
Jun 29 '13 at 9:14
source share

update: The main reason put on this castle is

its working :) most of the "mysterious exceptions" I've ever had, after this trick disappear




  • locking, as in this code, can help with your exception
  • as you mentioned earlier, the problem with using a text box is less.
  • in this code, SuggestAppend is working fine


  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
Feb 07 '09 at 21:54
source share
 if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text)) textBox3.AutoCompleteCustomSource.Add(textBox3.Text); 
0
Apr 28 '09 at 19:12
source share

Sam, do you understand this? I am facing the same situation. Clear () obviously throws an exception. I deleted the call to clear, and I get the right offer, although the collection continues to grow ...

In addition, regarding private members: you can access them by reflecting:

 PropertyInfo[] props = [object].GetType().GetProperties({flags go here}); props[0].SetValue(this, new object[] { 0 }); 
0
Apr 26 '10 at 18:39
source share

I came here initially looking for a solution, but now I have found my own.

The trick is not to call Clear () in the AutoCompleteCustomSource, but to delete all the elements in the for loop and then rebuild the list with the new data. In my case (an application for a collection of books), I take the names of the authors from the database with a specific starting letter, and not the entire batch. Please note that this will only work if part of the text field in the combo box is omitted.

  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
Jun 18 '10 at 17:10
source share

Do not try to do this, but for your specific case you can encode something like:

  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
Feb 27 2018-11-11T00:
source share

For me, the secret was to use the TextChanged event and not one of KeyDown / Up / Press, etc.

Update: After I had other problems with the dynamic change of AutoCompleteCustomSource, I eventually abandoned the use of the built-in Autocomplete function and implemented my own work in a much shorter time than I originally spent on it. There seems to be some issues in unmanaged code that implement the ComboBox control. In particular, I had problems starting the TextChanged event handler when necessary. I decided to use only OnKeyDown / Press / Up handlers in my custom implementation, and this seemed more reliable.

0
Sep 21 '11 at 20:41
source share

The best solution for this is to use combobox event handlers. Using textUpdate KeyDown DropDown and ChangeCommit , you can mimic autocompletemode and you can configure what to look for and what to display in the drop-down list.

I found this useful, but it is encoded in Visual C ++, and it is toolstripcombobox, but the concept is identical . In any case, there is a huge similarity between C # and C ++ in .net, and this should not be a problem in understanding the solution.

Individual autostart ToolStripCombobox in Visual C ++

0
Jan 07 '15 at 8:23
source share

This is a very old problem that I know, but it exists today. My workaround was to set the Autocomplete mode and the original properties to "none" and manually update the elements in the KeyUp event.

I am sure that it is hacked, but it works fine for me without problems for a long time, regardless of the speed of data entry, while the additional bonus of my hair starts to grow.

You can also choose, offer or offer and add. Hope this can help someone.

 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
Apr 23 '18 at 14:00
source share

use this 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
Dec 13 '13 at 10:50
source share



All Articles