I am not sure if anyone has tried doing it but binding a dictionary to a combobox and sorting it could yield some surprises. For example, there was a scenario where I was supposed to bind incoming data as dictionary into a combobox. The control should display only the values in the dropdown. On selecting any one, the key would be used as the selected value for further logic.

Initially when I bound the dictionary without any addition function it worked fine in one go. Now since the values in the dictionary were not alphabetically sorted, I thought setting combobox.sorted = true should do the job. I was wrong.

Here is the dictionary that I am using as an example. Notice that I have deliberately added key 7 on 3rd index so as to check if the sorting works.

Dictionary<int, string> dictBind = new Dictionary<int, string>();
dictBind.Add(1, "Value 1");
dictBind.Add(2, "Value 2");
dictBind.Add(7, "Value 7");
dictBind.Add(3, "Value 3");
dictBind.Add(4, "Value 4");
dictBind.Add(5, "Value 5");
dictBind.Add(6, "Value 7");

The approach that I was following was that I first initialized the ValueMember and DisplayMember properties of combobox and then assigned the binding data source. Below is the code:

cmbDictionary.ValueMember = "Key";
cmbDictionary.DisplayMember = "Value";
cmbDictionary.DataSource = new BindingSource(dictBind, null);

This works perfectly fine (see here) until I add a piece of code after DisplayMember assignment:

cmbDictionary.ValueMember = "Key";
cmbDictionary.DisplayMember = "Value";
cmbDictionary.Sorted = true;
cmbDictionary.DataSource = new BindingSource(dictBind, null);

Notice how the display value changes (see here). Instead of just displaying the values, it displays data as key, value and then sorts it. This was confusing to me as it worked perfectly fine in the previous case. I tried to dig a little by debugging the ComboBox class source code and noticed that the Sorted property setter does some extra work which might impact how the data is bound when the BindingSource constructor is called. You can set up VS to debug the source code and then try it out yourself. I was not able to identify cleanly but it looks like the data is returned the same for both cases. One difference that I could see was that when sorted is set to true before data source assignment, the RefreshItems method in the ComboBox class is called twice: Once in the Sorted property setter and second in the OnDataSourceChanged event. This might have some impact on the DisplayMember value of the combobox.

Workarounds

To avoid such situations there are some ways that you can use to bind a key value pair to combobox.

1. Bind a sorted data source: This can be done by simply ordering the key value pair as per the key or value (or a property in case of a custom object) using the OrderyBy extension method. Below is the change that you can make to the combobox data source assignement:

cmbDictionary.DataSource = new BindingSource(dictBind.OrderBy(x=>x.Value), null);

2. Set the DisplayMember and ValueMember properties after data source: This I think is the correct way to bind a combobox to any data source. This will automatically solve the Display issue. Below is the refactored code.

cmbDictionary.Sorted = true;
cmbDictionary.DataSource = new BindingSource(dictBind, null);

/* Set these properties after the data is bound. */

cmbDictionary.ValueMember = "Key";
cmbDictionary.DisplayMember = "Value";

Note that you cannot set combobox.sorted = true after the data source is bound. This will throw an exception as can be seen in the source code below taken from the ComboBox.cs class (reference):

public bool Sorted {
            get {
                return sorted;
            }
            set {
                if (sorted != value) {
                    if (this.DataSource != null && value) {
                        throw new ArgumentException(SR.GetString(SR.ComboBoxSortWithDataSource));
                    }
 
                    sorted = value;
                    RefreshItems();
                    SelectedIndex = -1;
                }
            }
        }

3. Create an instance of the BindingSource Class and assign sort column: This can also be done if one wants to avoid the combobox sorted property completely. It is also better since the developer gets freedom to assign custom columns as sort indicators in case of custom objects:

BindingSource bs = new BindingSource();
bs.DataSource = dictBind;
bs.Sort = "Key";

cmbDictionary.DataSource = bs;

cmbDictionary.ValueMember = "Key";
cmbDictionary.DisplayMember = "Value";

In all of these cases I got the correct value bound in the combobox (see here). I had searched the net for any guidance for the issue that I faced initially but had to resort to debugging source code to find the root cause. Although, there was a tip that I found on CodeProject. But I had to dig in to find out more. Didn’t turn out to be a bad thing at all. :)

Do let me know similar problems or situations that you have encountered or are there better ways to bind key value pairs as data source.