C# Dynamic ComboBox.SelectedIndex

GrammatonCleric

Well-Known Member
Joined
Nov 17, 2015
Messages
167
Reaction score
0
Location
Joburg
I'm a SQL / BI Developer by profession, so please bear with me.

I fill a WinForms ComboBox with values from a table in my database using an async Task<Dictionary<int, string>>. This happens in my constructor. I have a switch..case statement on the SelectedIndexChanged EventHandler to fill a DataSet when an item in the ComboBox is selected. I will eventually change this to a generic List object to consume less memory etc.

The problem I have is...if I insert a new value into the table that populates the ComboBox, the indexes change as the items in the ComboBox are ordered alphabetically and the switch..case statement isn't viable anymore because the indexes are different each new item added to the table / ComboBox.

Is there a way to get around this?

I'm considering using the 'key' in the Dictionary as the 'value' are the items shown in the ComboBox, but the ComboBox SelectedIndex property is zero-based. I'm not too sure if the Dictionary 'key' is zero-based as well, but I'm not too sure how to do this.

Any guidance from a C# pro would be super!
 
Everything is zero based in c#.You seem to be on the right track.
 
Not a good idea to use an index reference for the combobox or list types of you know the items will change. Key value pairs, best to stick with the key rather
 
So what I think I should do is loop through the Dictionary items in the SelectedIndexChanged EventHandler and set the SelectedIndex based on SelectedItem or SelectedValue?
 
The StackOverflow solution doesn't really do what I need :(

What I need is (on ComboBox.SelectedIndexChanged):

foreach (KeyValuePair<int, string> item in dictSections)
{
//Based on the SelectedValue, call SetControls()
}

Here's my method:

void SetControls(string process, string processText)
{
_process = process;
Cursor = Cursors.WaitCursor;
pbLoader.Visible = true;
lblProcess.Visible = true;
lblProcess.Text = processText + "...";
cmbSection.Enabled = false;
btnExportData.Enabled = false;
}

So, for whatever item is selected from the ComboBox, the SetControls() method is called with different string values. Based on the value(s) of these strings, the DataSet is populated with different stored procedures.

The SetControls() method is already referenced 10 times.

I use it like this at the moment:

switch (cmbSection.SelectedIndex) //SelectedIndexChanged EventHandler
{
case 7:
if (!backgroundWorker1.IsBusy)
{
SetControls("PopulatePolicyDetailsDataSet", "Building Policy Details DataSet");
backgroundWorker1.RunWorkerAsync();
}
break;
}

...And

switch (cmbSection.SelectedIndex) //Button_Click EventHandler
{
case 7:
if (!backgroundWorker1.IsBusy)
{
SetControls("WritePolicyDetailsFile", "Exporting Policy Data to File");
backgroundWorker1.RunWorkerAsync();
}
break;
}



How would I achieve the same result using the foreach iteration depending on the SelectedValue?
 
Few things:
  • Never use a record index, far too volatile as you've realized. Ideally your displayed value should be unique, otherwise you have to concatenate the table fields that facilitate that for the user; then ensure that your Dictionary is populated with a matching unique index field i.e. unique index key as opposed to the your record index
  • For performance reasons make sure that your unique key is indexed. If your table doesn't have a unique key, consider adding one & indexing this (e.g. GUID)
  • Tying up the selected value versus your table's unique key is a simple case of using your Dictionary to lookup up the key for the selected value.
 
Last edited:
How would I achieve the same result using the foreach iteration depending on the SelectedValue?

You're still trying to use an int/string KeyValuePair. Unless the int is the database ID (and not the index), change it up.

SetControls(dictSections.Where(x => x.Key == cmbSection.SelectedIndex).FirstOrDefault().Value, "Not sure where you are going to get this description from");
 
The key is the PK from my DB table.

Your LINQ statement always defaults to the last element in the ComboBox for some reason.

I got this far, and I'm trying to figure it out further:

int i = 0;

foreach (KeyValuePair<int, string> item in dictSections)
{
if (i == cmbSection.SelectedIndex)
{
if (!backgroundWorker1.IsBusy)
{
SetControls("Populate" + item.Value.ToString().Replace(" ", "") + "DataSet", "Building " + item.Value + " DataSet");
backgroundWorker1.RunWorkerAsync();
}
}
i++;
}

This works for the SlectedIndexChanged EventHandler. I just have to figure out how to apply similar logic to the rest of my switch..case statements.

As you can see, the second parameter is used to set a label's text on the form to let the user know what is happening at a specific point in the process
 
What you want to do is create yourself a simple class, that will contain all the information you require; You will add instances of this class to your combo box.
Then, on SelectedIndexChanged, you will, check whether SelectedValue isnt null first.
If not null, you will cast SelectedValue to your class you created, and use the data you were interested in.
Example:
class MyComboClass
{
public int MyPrimaryKeyOrWhatever;
public string MyName;

public override ToString()
{
return MyName; // so that you see a friendly name in the combo box...
}
}

void populateMyCombo()
{
foreach ( record in myDataSet)
{
myCombo.Items.Add( new MyComboClass(){ MyPrimaryKeyOrWhatever = record["whatever"], MyName = record["myname"] } );
}
}

void myCombo_SelectedIndexChanged(.....)
{
if ( myCombo.SelectedItem != null )
{
MyComboClass x = myCombo.SelectedItem as MyComboClass;
if ( x.MyPrimaryKeyOrWhatever == whatever )
{
// do whatever.
}
}
}


EDIT:
With this mechanism, you can 'contain' absolutely anything you want in a combo box; you don't need to do any other special footwork/panelbeating to get the job done.
 
Last edited:
Its a bit hard to reverse engineer your problem over a small post, but try:

1. Create a container for your SetControls() calls, e.g. a dictionary of key/parameters which will use the same key type as your DB, for example
class SetControlValue
{
string storedProc; // = "WritePolicyDetailsFile",
string description; // = "Exporting Policy Data to File"
...
}
Dictionary<int, SetControlValue> controls ...
Add to the dictionary from your DB, e.g. controls.Add(1, new SetControlValue("WritePolicyDetailsFile", "Exporting Policy Data to File"));
You can wrap that into a method, load them from a file or store the extra values also in your db, then dynamically create them at runtime.

Later when you select the Combo item from the frontend you get the name/id (as below) and call SetControls(controls[id].storedProc, controls[id].description) for all cases, thereby eliminating the switch.


2. There are many ways to manipulate a ComboBox. If you want to only store string values in the ComboBox, then make a reverse lookup that'll give you the key from the text (ie. a Dictionary where you send in "WritePolicyDetailsFile" and it spits out a 1 for the id). Or use the above description by MrJax to make a container of key/item that you then place into the ComboBox.
Some example:
// string only using items+selectedindex
string value = comboBox.Items[comboBox.SelectedIndex].ToString();

// MrJax container using selectedItem
MyComboClass comboItem = comboBox.SelectedItem;
SetControls(comboItem.key, comboItem.name);
...
 
IMO There's no need for a foreach, consider First() or FirstOrDefault() with linq i.e. Use that key to load your matching dataset.
Btw just to pick up on something you asked previously; a dictionary key is something you provided i.e. not usually generated, because then it could just have been an array.
For example:
  • Key = unique record id in your table
  • Value = the value you want displayed in the combobox.

First() or FirstOrDefault() allow you take the value a user selected in the combobox and look up it's key in the Dictionary; key then possibly passed to your dataset's SP as an argument.

I'm assuming a lot here re lack of info, but I'd be surprised if you still need the switch/case, or nested ifs
 
Last edited:
Thanks for all the suggestions guys. I'll take what all of you said and find a way to make this work, but it may take some time as this isn't the only thing I'm working on at the moment.

As I said in the beginning, my primary function is SQL / BI Dev. I just didn't see the harm in broadening my scope :)

I'll post my solution as soon as I've completed the app.

You guys are awesome!!
 
Top
Sign up to the MyBroadband newsletter
X