1

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 :)

Ren
  • 437
  • 4
  • 17
  • Please add the code that you use to reproduce this. – Adrian Fâciu Jun 10 '13 at 06:52
  • Plz gimme 5 mins @Adrian. I will paste the parts of the code, relevant to this context. – Ren Jun 10 '13 at 06:55
  • 1
    I believe that in the above code DropdownsCollection should be defined as IList or BindingList in order to compile... So you have a specific order of the items in DropdownsCollection but on the UI a different order is shown ? Is this correct ? – Adrian Fâciu Jun 10 '13 at 08:03
  • Sorry Adrian, I meant to type BindingList. I hand-typed that part of the code, instead of copy-pasting. `So you have a specific order of the items in DropdownsCollection but on the UI a different order is shown ? Is this correct` You got it right. That's exactly the problem. Also, the item which is selected has a value that is not of it.. though the Binding list has everything as expected. – Ren Jun 10 '13 at 08:21
  • 1
    By default data binding will not change the sort order of the items, so I guess the problem lies somewhere else, possibly in the way you handle the items. If initially the collection contains 'A', 'B' and 'C' and you remove 'B' and add 'D', would you expect to have the order 'A','C','D' ? – Adrian Fâciu Jun 10 '13 at 08:30
  • Yes Adrian.. The BindingList has the order 'A', 'C', 'D'. But the ComboBox has 'D', 'A', 'C'... I am setting the text of some control to the selectedValue of the Combobox in the SelectedIndexChanged handler. It shows the Value of an item, as if the order were 'A', 'C', 'D'. I mean, when 'D' is selected, it shows the Selected Value as that of 'A', and so on.. – Ren Jun 10 '13 at 08:35
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31511/discussion-between-venkat-renuka-prasad-and-adrian-faciu) – Ren Jun 10 '13 at 08:45

2 Answers2

1

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.

Community
  • 1
  • 1
Adrian Fâciu
  • 12,414
  • 3
  • 53
  • 68
  • Thanks a lot Adrian, will try that. It will take some time to check for myself.. as I am working on some other module at the moment. – Ren Jun 10 '13 at 08:53
  • Adrian, I used OrderedDictionary. I could understand that it could be a better DataStructure in my case. Coming to the second problem that you mentioned.. I am changing only a property (FrontEndName) of the Value member of the OrderedDictionary, for the same key. When I am debugging, the OrderedDictionary shows keys and values as they are expected. The bindingList is as expected. But the combobox still shows some item as selected, and it's SelectedValue is that of something else. :( – Ren Jun 10 '13 at 09:55
  • I will try removing the DictionaryEntry and adding it again.., instead of modifying it's value based on its key. I still don't get why this could be happening.. – Ren Jun 10 '13 at 09:58
  • `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.` There is a lot of other data that is in the Value of the DictionaryEntry (the DropdownEntity object).. only the property FrontEndName is changed. I wanted the rest of the data in that object to be intact. That was the reason I modify I keep the Dictionary entry and just modify it's value. – Ren Jun 10 '13 at 10:22
  • Eventhough I removed the DictionaryEntry by key and added a new one it, with the Value, of the existing DropdownEntity object, the problem persists. The utterly strange thing is that the BindingList is fine, while the Combobox items are messed. – Ren Jun 10 '13 at 10:25
1

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.. :)

Ren
  • 437
  • 4
  • 17