Initializing a combobox to a blank value might be harder than you think

Combobox troubles again today. No matter how hard I tried to have Comboboxes in my dialog form to initially display blank (SelectedIndex = -1), as soon as the dialog was displayed, the combos would always show the first item in the List that populated it. Despite being set to -1 explicitly. It was driving me nuts.

When created at runtime using a List as DataSource, getting this (empty combos) might be harder than you think

The situation: I had a dialog form that should have a variable number of comboboxes on it, determined at runtime. So the form had an Init method where I dynamically added the comboboxes, and set its DataSource to a List of items (directly, no BindingSource). The form also has a LoadData method, that sets the SelectedIndex of each combo to the actual value from the data, or to -1 of it was unknown or uninitialized at that time. This was to be able to make the combo appear blank instead of preselecting the first item, which was undesired. The caller of the dialog executes Init(), LoadData() and finally ShowDialog().

I was expecting a blank combobox, but it showed the first item in the list preselected. I triple checked that the SelectedIndex was correctly set to -1 at the end of LoadData(), but as soon as the form was displayed, it was 0. What the *** happened there in between and “who” was secretly setting my property!?

Fortunately I had two great tools available, the first being ReSharper 6 and second being Microsoft allowing to download debugging .Net Framework source code. So I added the following to my custom combox class:

// Note: this override is redundant, but can be useful in debugging where an index gets set.
public override int SelectedIndex {
  get {
    return base.SelectedIndex;
  set {
    base.SelectedIndex = value;

Then, I set a breakpoint on the setter with a condition of “value == 0” and launched the program and ran the dialog. The breakpoint was hit:

OK, so the BindingContextChanged event got fired. Sifting through the code downloaded automatically from Microsoft by ReSharper, I learned that this fires ListControl.SetDataConnection with a bool parameter called ‘force’ set to true, which in turn resets SelectedIndex to the current position of the internal DataManager, which is indeed 0 at that point.

But why was BindingContextChanged fired? I traced this call all the way back to Control.CreateControl(), which calls this by default and also notifies all children in the controlsCollection (via OnParentBindingContextChanged). And there it was: ParentBindingContextChanged only calls BindingContextChanged on its children, if the BindingManager was previously null. Because I was creating the Combobox at runtime and I did not explicitly set it, this was the case.

So the solution (or workaround?), turned out to be deceivingly simple. I now do this in Init(), after I assign the List to the DataSource:

combo.BindingContext = this.BindingContext(); // "this" being the Form 

That solves my problem: BindingContextChanged now no longer fires when the form is displayed. It still fires, and still resets SelectedIndex to 0, but earlier, when I trigger it, in Init. That all happens before I set it to -1 in LoadData. So when the form is displayed, it is still -1.

I tested, and yes, the comboboxes were now blank as they should be. A couple of hours wasted on something that appears to be very simple, but I learned something of the inner workings of the .Net Framework along the way.


Over Shiftkey
I am Maarten, owner of and chief software developer for Shiftkey software development from The Netherlands. I will be writing mainly about things I run into when programming in C# or Delphi.

Geef een reactie

Vul je gegevens in of klik op een icoon om in te loggen. logo

Je reageert onder je account. Log uit / Bijwerken )


Je reageert onder je Twitter account. Log uit / Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log uit / Bijwerken )

Google+ photo

Je reageert onder je Google+ account. Log uit / Bijwerken )

Verbinden met %s

%d bloggers liken dit: