+1  A: 

I think you might want to get out the reflector and look at overriding the autocomplete behaviour in the combobox itself. I'm certain the autocompletion would call a function which accesses the autocomplete list. If you can find this function and override it, you can use any behaviour you want.

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

Spence
A: 

I have no experience in doing that sort of thing, but using a reflector I found a couple of methods in the ComboBox class that look interesting - GetStringsForAutoComplete() and SetAutoComplete(), but they are both private so I can't access them from a derived class. I'm not sure what to do now.

A quick tip: Stack Overflow's answers don't work as a discussion thread - they get moved out of order to move the highest rated answers to the top. Instead of adding an answer like this, edit your original question to add a clarification to the end.
Bevan
+1  A: 

I haven't test this, but it may be worth a shot.

Instead of clearing the AutoCompleteCustomSource, double buffer by keeping two instances. When the text changes, call GetNameSuggestions() and build the strings for the one that's not currently being used, then set ComboName.AutoCompleteCustomSource to the one you just set up.

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;
}
Adam Haile
A: 

Thanks for the effort, but that was one of the things I've already tried. I forgot to mention that in the list in my original post. I'll add it now:

  • Replacing the existing AutoCompleteCustomSource with a new AutoCompleteStringCollection each time.
A: 

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?

try to use lock.
Avram
What should I 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.
add these details to the question, don't add them as answers
spoon16
I didn't know I was supposed to do that when I first asked this question, but I have since done it and now can't remove these answers.
A: 

update: main reason to put the lock on this place is

its working :) most of "mysterious exception" that i ever have, after this trick disappear


  1. the lock like in this code, can help with your exception
  2. as you mention before, there is less problem with using textbox
  3. in this code, SuggestAppend 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;
            }
        }
    }
Avram
I haven't tried that exact code, but I think it is basically the same as replacing the existing AutoCompleteCustomSource with a new AutoCompleteStringCollection each time, which I have tried and which throws occasional exceptions.
That lock wouldn't prevent the exception because the TextChanged handler isn't re-entrant anyway - I've checked this.
Also, I've tried something similar (only occasionally changing the AutoCompleteStringCollection, instead of on every character), and I still get exceptions under certain circumstances. There may be a race condition in the underlying code, so I'm not comfortable using this in production environments
A: 
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
A: 

Sam, have you got this figured out? I am running into the same situation. Clear() seems to cause the exception. I removed the call to clear and I'm getting the correct suggestions event though the collection keeps on growing...

Also, regarding the private members: you can access them using reflection:

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

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

The trick is not to call Clear() on the AutoCompleteCustomSource but to remove all items in a for loop and then rebuild the list with the new data. In my case (a book collection application) I'm retrieving author names from a database with a specific starting letter, instead of the whole lot. Note that this will only work if the textbox part of the combobox is or has become empty.

    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);
        }
    }
Jim Cramer
Further testing proved this solution to show erratic behaviour, even after correcting the erroneous "clear" loop which should have been:while (authorsAutoComplete.Count > 0) authorsAutoComplete.RemoveAt(0);Sadly, I have no other solution.
Jim Cramer