The BindingList Datasource of a Combobox refreshes correctly but the Combobox displays items in the wrong order

StackOverflow https://stackoverflow.com/questions/17013035

  •  31-05-2022
  •  | 
  •  

Question

I have a BindingList< KeyValuePair < string, string > > that is bound to a ComboBox control. Based on some conditions, the BindingList will be added a new KeyValuePair. Now, the Newly added item shows up at index 0 of the Combobox, instead of at the end.

While debugging, I found that the BindingList has got the right order. (i.e, the new KeyValuePair is appended)

Also, I check the SelectedValue of the ComboBox in it's SelectedIndexChanged handler and it seems to be not of the ListItem that got selected. Instead, it is that of the supposed ListItem, if the ComboBox had got the right order as in its DataSource, - the BindingList..

The code is a small part of a large project.. Plz let me know if the question is not clear. I can put the relevant parts of the code as per our context.

How could something like this happen? What can I do differently?

I have this class something like this.

public class DropdownEntity
{
    //removed all except one members and properties

    private string frontEndName
    public string FrontEndName
    {
        get {return this.frontEndName; }
        set {this.frontEndName= value; }
    }

    //One Constructor
    public DropdownEntity(string _frontEndName)
    {
        this.FrontEndName = _frontEndName;

        //Removed code which initializes several members...
    }

    //All methods removed..

    public override string ToString()
    {
        return frontEndName;
    }
}

In my windows form, I have a tab control with several tabs. In one of the tabs pages, I have a DataGridView. The user is supposed to edit the cells and click on a Next - button. Then, some processing will be done, and the TabControl will be navigated to the next tab page.

The next tab page has the combobox that has the problem I mentioned. This page also has a back button, which will take back.. the user can modify the gridview cells again.. and click on the next button. This is when the order gets messed up.

I am posting here the Click event handler of the Next Button.. Along with the class, with the rest of the code removed.

public partial class AddUpdateWizard : Form
{        
    //Removed all members..

    BindingList<KeyValuePair<string, string>> DropdownsCollection;
    Dictionary<string, DropdownEntity> DropdownsDict;

    //Defined in a partial definition of the class..
    DataGridView SPInsertGridView = new DataGridView();
    ComboBox DropdownsCmbBox = new ComboBox();

    Button NextBtn2 = new Button();
    Button BackBtn3 = new Button();
    //Of course these controls are added to one of the panels

    public AddUpdateWizard(MainForm mainForm)
    {
        InitializeComponent();
        DropdownsDict = new Dictionary<string, DropdownEntity>();
    }

    private void NextBtn2_Click(object sender, EventArgs e)
    {
        string sqlArgName;
        string frontEndName;
        string fieldType;

        for (int i = 0; i < SPInsertGridView.Rows.Count; i++)
        {
            sqlArgName = "";
            frontEndName = "";
            fieldType = "";

            sqlArgName = SPInsertGridView.Rows[i].Cells["InsertArgName"].Value.ToString().Trim();

            if (SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value != null)
            {
                frontEndName = SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value.ToString().Trim();
            }

            if (SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value != null)
            {
                fieldType = SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value.ToString().Trim();
            }

            //I could have used an enum here, but this is better.. for many reasons.
            if (fieldType == "DROPDOWN")
            {
                if (!DropdownsDict.ContainsKey(sqlArgName))
                    DropdownsDict.Add(sqlArgName, new DropdownEntity(frontEndName));
                else
                    DropdownsDict[sqlArgName].FrontEndName = frontEndName;
            }
            else
            {
                if (fieldType == "NONE")
                    nonFieldCount++;

                if (DropdownsDict.ContainsKey(sqlArgName))
                {
                    DropdownsDict.Remove(sqlArgName);
                }
            }

        }

        //DropdownsCollection is a BindingList<KeyValuePair<string, string>>.
        //key in the BindingList KeyValuePair will be that of the dictionary.
        //The value will be from the ToString() function of the object in the Dictionary. 

        DropdownsCollection = new BindingList<KeyValuePair<string,string>>(DropdownsDict.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.ToString())).ToList());

        DropdownsCmbBox.DataSource = DropdownsCollection;

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

        //Go to the next tab
        hiddenVirtualTabs1.SelectedIndex++;
    }

    private void BackBtn3_Click(object sender, EventArgs e)
    {
        hiddenVirtualTabs1.SelectedIndex--;
    }

    //On Selected Index Changed of the mentioned Combobox..        
    private void DropdownsCmbBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (DropdownsCmbBox.SelectedValue != null)
        {
            if (DropdownsDict.ContainsKey((DropdownsCmbBox.SelectedValue.ToString())))
            {
                var dropdownEntity = DropdownsDict[DropdownsCmbBox.SelectedValue.ToString()];

                DropdownEntityGB.Text = "Populate Dropdowns - " + dropdownEntity.ToString();

                //Rest of the code here..
                //I see that the Datasource of this ComboBox has got the items in the right order.
                // The Combobox's SelectedValue is not that of the selected item. Very Strange behavior!!

            }
        }
    }
}

The very first time the user clicks the Next Button, it's fine. But if he clicks the Back Button again and changes the Data Grid View cells.. The order will be gone.

I know, it can be frustrating to look at. It's a huge thing to ask for help. Any help would be greatly appreciated!

Please let me know if you need elaboration at any part.

Thanks a lot :)

Was it helpful?

Solution 2

The Sorted Property of the Combobox is set to True! I didn't check that until now. I messed up. Terribly sorry for wasting your time Adrian. Thanks a lot for putting up with my mess here.. :)

OTHER TIPS

I think you have two problems here.

First, if you want to retain the order of the items you should use an OrderedDictionary instead of a regular one. A normal collection will not retain the order of the items when you use Remove method. You can see more info about this related to List here.

You could use such dictionary like this:

DropDownDict = new OrderedDictionary();
// Add method will work as expected (as you have it now)

// Below you have to cast it before using Select
DropDownCollection = new BindingList<KeyValuePair<string, string>>(DropDownDict.Cast<DictionaryEntry>().Select(kvp => new KeyValuePair<string, string>(kvp.Key.ToString(), kvp.Value.ToString())).ToList());

The second problem could be that you change the display name (FrontEndName) of already existing items, but the key is preserved. When you add a new item, try to remove the old one that you're not using anymore and add a new item.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top