0

I have a ComboBox in my WinForms application that has the DropDown style, or in other words it allows a user to type a value, or select it from a drop-down list. It looks as such:

enter image description here

My goal is to select an item from the drop-down list based on its index. So I do this:

//I know for sure that i >= 0 && i < comboBox.Items.Count
comboBox.SelectedIndex = i;  //i = index of the existing item

This works just fine, unless some time after the line above I display a message:

MessageBox.Show("Error");

In this case the item in the combo box got selected roughly in 80% of the time during my tests on this computer. So there's clearly some timing issue here.

What I need to know is what is the "sure" way of selecting a ComboBox item? (And by that I mean, being sure that it is selected when the next line of code following it is executed, or do it synchronously.)

PS. I know that I can simply assign text to its Text property, but that's not what I'm asking for. You see in my ComboBox implementation, I may assign custom objects to each item, plus I rely on many selection-based events, such as SelectedIndexChanged.

PS2. I'm not sure if this affects only combo boxes with the DropDown style, or all of them.

ahmd0
  • 16,633
  • 33
  • 137
  • 233

3 Answers3

1

Well, you could do that with SelectedValue and SelectedItem when you have objects as the source of your ComboBox.

Have a look at this article : Understanding SelectedValue, SelectedValuePath, SelectedItem & DisplayMemberPath. You got code examples you can run as well.

From the article:

SelectedItem : This will return the currently selected item in thelist/combobox/container . This is an Object as you can see (since my list holds objects). If your List/Combobox/etc contains a list of strings, it'll be a string. If it contains ints, it'll be an int.

SelectedValuePath : Setting this will make the property SelectedValue return the value of the property you have selected here. In our example, selecting "ShapeColor" will make the SelectedValue return only the color, and not the whole shape object.

SelectedValue : If you want only a part of an object, set the above property, and you'll get the value of that property here. Note that if SelectedValuePath is not used, this is the same as using SelectedItem.

DisplayMemberPath : Setting this to a property of an object, will cause the GUI to show that property when the class is selected instead of seeing the Class name or your Class ToString()method (which should always be provided. Look at Item 5 in this book's ToC ).

Disclaimer: I'm the author of the article ...

Noctis
  • 11,507
  • 3
  • 43
  • 82
  • I appreciate it. Since you wrote an article about that control have you witnessed the behavior that I'm describing? – ahmd0 Nov 07 '13 at 00:27
  • I don't like to rely on `SelecetedIndex`. If you need the port number, and assuming it's a property on the object you assign to the `ComboBox`, set the `SelectedValuePath` on it, and just use the `SelectedValue`, avoiding the need to parse the object at the index. Having said that, I didn't encountered issues with the selection done my way ... – Noctis Nov 07 '13 at 00:33
1

My bet is that the event that fires when you change that selection isn't being handled before you open the MessageBox.

Try adding this:

Application.DoEvents();

... before you call MessageBox.Show(); and see if that helps. If it does, you can try to make the control redraw itself synchronously by

comboBox.Refresh();

You can try comboBox.Invoke if you're threading. Also, PerformLayout on the Form/Control can sometimes force these kinds of things to happen right away rather than awaiting a paint message.

A MessageBox.Show stops all Form processing in its tracks. The ultimate solution may be to put it in a different place, when you're sure that the combobox has done everything it needs to do.

David Schwartz
  • 1,956
  • 19
  • 28
1

Your message box interrupts the ComboBox processing to update the selected index. This behaviour is intermittent because forms and controls, like a ComboBox and a message box, depend on the Windows messaging system. There may be hundreds of messages (sometimes more, sometimes less) generated by other parts of the form around the same time as your selection change and message box. When there is a high volume of messages the system may fail to process some, therefore the selected index does not change.

Two options to avoid this problem:

  • Set SelectedIndex after your sanity check of new index i that shows a message box.
  • Do your sanity check asynchronously using BeginInvoke (example below), ie. after the system has completed message processing for the changed selected index.

One way to use BeginInvoke:

comboBox.SelectedIndex = i;

// Sanity check new index i after message processing for the combo box.
comboBox.BeginInvoke(delegate()
{
    if (/* sanity check fails */)
    {
        MessageBox("Error");
    }   
});

Edit: You may get the behaviour you want by using Application.DoEvents, as another answer suggests, but I would steer clear of this approach.

Community
  • 1
  • 1
groverboy
  • 1,133
  • 8
  • 20
  • I want to point out that I am calling all this from the same thread. Do I still need to worry about `BeginInvoke`? – ahmd0 Nov 08 '13 at 00:45
  • Not sure what you mean by worry. You can call `BeginInvoke` on the same (UI) thread to execute that code _asynchronously_, ie. after the control has finished its current workload (change selected index). `BeginInvoke` is not only for non-UI threads. – groverboy Nov 08 '13 at 02:46