I have a combobox in C# and I want to use auto complete suggestions with it, however I want to be able to change the auto complete entries as the user types, because the possible valid entries are far too numerous to populate the AutoCompleteStringCollection
at startup.
As an example, suppose I'm letting the user type in a name. I have a list of possible first names ("Joe", "John") and a list of surnames ("Bloggs", "Smith"), but if I have a thousand of each, then that would be a million possible strings - too many to put in the auto complete entries. So initially I want to have just the first names as suggestions ("Joe", "John") , and then once the user has typed the first name, ("Joe"), I want to remove the existing auto complete entries and replace them with a new set consisting of the chosen first name followed by the possible surnames ("Joe Bloggs", "Joe Smith"). In order to do 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 the call to Clear() causes the auto complete mechanism to "turn off" until the next character appears in the combo box, but of course when the next character appears the above code calls Clear() again, so the user never actually sees the auto complete functionality. It also causes the entire contents of the combo box to become selected, so between every keypress you have to deselect the existing text, which makes it unusable. If I remove the call to Clear() then the auto complete works, but it seems that then the AddRange()
call has no effect, because the new suggestions that I add do not appear in the auto complete dropdown.
I have been searching for a solution to this, and seen various things suggested, but I cannot get any of them to work - either the auto complete functionality appears disabled, or new strings do not appear. Here is a list of things I have tried:
- Calling
BeginUpdate()
before changing the strings and EndUpdate()
afterward.
- Calling
Remove()
on all the existing strings instead of Clear().
- Clearing the text from the combobox while I update the strings, and adding it back afterward.
- Setting the
AutoCompleteMode
to "None" while I change the strings, and setting it back to "SuggestAppend" afterwards.
- Hooking the
TextUpdate
or KeyPress
event instead of TextChanged
.
- Replacing the existing
AutoCompleteCustomSource
with a new AutoCompleteStringCollection
each time.
None of these helped, even in various combinations. Spence suggested that I try overriding the ComboBox
function that gets the list of strings to use in auto complete. Using a reflector I found a couple of methods in the ComboBox
class that look promising - GetStringsForAutoComplete()
and SetAutoComplete()
, but they are both private so I can't access them from a derived class. I couldn't take that any further.
I tried replacing the ComboBox
with a TextBox
, because the auto complete interface is the same, and I found that the behaviour is slightly different. With the TextBox
it appears to work better, in that the Append part of the auto complete works properly, but the Suggest part doesn't - the suggestion box briefly flashes to life but then immediately disappears.
So I thought "Okay, I'll live without the Suggest functionality and just use Append instead", however when I set the AutoCompleteMode
to Append, I get an access violation exception. The same thing happens with Suggest - the only mode that doesn't throw exceptions is SuggestAppend
, even though the Suggest part doesn't then behave correctly.
I thought that it was supposed to be impossible to get access violation exceptions when using C# managed code. Avram suggested I use "lock" to fix this, but I don't know what I should lock - the only thing that has a SyncRoot member is the AutoCompleteStringCollection
, and locking that doesn't prevent the access violation exceptions. I also tried locking the ComboBox
or TextBox
, but that didn't help either. As I understand it, lock only prevents other locks, so if the underlying code isn't using lock then my using it won't make any difference.
The upshot of all this is that I can't currently use a TextBox
or a ComboBox
with dynamic auto complete. Does anyone have any insights into how I could achieve this?
Update:
I still haven't got this working, but I have found out some more. Maybe some of this will inspire someone else to come up with a solution.
I tried replacing the ComboBox
with a TextBox
, because the auto complete interface is the same, and I found that the behaviour is slightly different. With the TextBox
it appears to work better, in that the Append part of the auto complete works properly, but the Suggest part doesn't - the suggestion box briefly flashes to life but then immediately disappears.
So I thought "Okay, I'll live without the Suggest functionality and just use Append instead," however when I set the AutoCompleteMode
to Append, I get an access violation exception. The same thing happens with Suggest - the only mode that doesn't throw exceptions is SuggestAppend
, even though the Suggest part doesn't then behave correctly.
I thought that it was supposed to be impossible to get access violation exceptions when using C# managed code, but anyway, the upshot is that I can't currently use a TextBox
or a ComboBox
with any kind of dynamic auto complete. Does anyone have any insights into how I could achieve this?
Update 2:
After trying various other things such as changing the autocomplete in a worker thread, and using BeginInvoke()
to simulate PostMessage() type behaviour, I finally gave up and just implemented my own auto complete dropdown using a list box. It's much more responsive than the built-in one, and I spent less time doing that than I did trying to get the built-in one to work, so the lesson for anyone else who wants this behaviour is - you're probably better off implementing it yourself.
See Question&Answers more detail:
os