Thursday, April 21, 2005

WinForms ComboBox ItemData

{

For the most part, I work with web based applications, in which a combo box works as you would expect. The class System.Web.UI.WebControls.DropDownList has an ListItemsCollection which consists of ListItem types. So you can dump data into it using the ListItem class which accepts, in the constructor, arguments for text/value pairs. Your code would look something like this:

ListItem li = new ListItem("MyText", "MyValue");
MyDropDownList.Items.Add(li);


You can shorten it with an anonymous instance of the ListItem:

MyDropDownList.Items.Add(new ListItem("MyText", "MyValue"));

Hence, a lot of code people write to populate these from a database looks like this:

// code to connect, build command, etc...
SqlDataReader rsData = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while(rsData.Read()){
MyDropDownList.Items.Add(new ListItem(rsData["TextFld"].ToString(), rs["ValueFld"].ToString()));
}
rsData.Close(); // never forget to close

Another way to do the same thing:

//code to connect, build command, etc...
SqlDataReader rsData = cmd.ExecuteReader(CommandBehavior.CloseConnection);
MyDropDownList.DataTextField = "TextFld";
MyDropDownList.DataValueField = "ValueFld";
MyDropDownList.DataSource = rsData;
MyDropDownList.DataBind();
rsData.Close(); // never forget to close

Whichever your fancy, the bottom line is that the DropDownList behaves as you expect it to: some text that displays to the user and an associated value which you can reference programmatically.


In the Windows.Forms namespace, used for so called WinForms applications, the class System.Windows.Forms.ComboBox is the equivalent of the drop down list. However, the collection it uses for its items is different; it only seems to have a single value associated with it. How does one map text/value pairs?

The underlying collection used by the WinForms ComboBox is of type ObjectCollection. Not too surprisingly, this is a collection of objects. You can, therefore, store any type of object as an item in this drop down list. For example, if I was keeping track of customer statuses, I may have the following data in a lookup table:





CUSTOMERSTATUSES
STATUSNAMESTATUSCODE
ACTIVE LOANACT
PAST DUEDEL

I want a drop down with the status name for the user, but in my code I really care about the status code, not the name. The first thing I can do is build an object to reflect these values:

class StatusData{
private string _StatusName;
private string _StatusValue;

public property StatusName{
get{return _StatusName;}
set{_StatusName = value;}
}
public property StatusValue{
get{return _StatusValue;}
set{_StatusValue = value;}
}
}

I want to add these objects to the drop down, but I want the status name to be displayed. This is the purpose of the DisplayMember property of the System.Windows.Forms.ComboBox class. It will allow me to specify a property to utilize for display on an object that it's storing:

MyWinFormsDropDown.DisplayMember = "StatusName";

After I've specified that, I can then store all my Status objects as references in the ObjectCollection of the WinForms drop down list:

// build collection earlier
foreach(StatusData sd in StatusDataFromDB){
MyWinFormsDropDown.Items.Add(sd);
}


Because I've already assigned the DisplayMember property, it knows which member of the hosted object to use to display. So how do I retrieve the value of the object StatusData that I've stored? Easy: cast the selected item from the drop down list, and then get any property that you want (in this case, we'll get the value).

// in an event somewhere
StatusData s = (StatusData)MyWinFormsDropDown.SelectedItem;
string val = s.StatusValue;

Seems like a massive chore, but if you think about it, there are already text/value datastructures that you can use without having to build unique classes for everything you want in a drop down list. For example, the handy System.Collections.DictionaryEntry would suffice. Try this on for size:

DictionaryEntry d = new DictionaryEntry();
d.Key = "TestName";
d.Value = "TestValue";

MyWinFormsDropDown.DisplayMember = "Key";
MyWinFormsDropDown.Items.Add(d);

Now, rather than building your own custom classes, you can use this provided class instead. But, the more thinking you do about it, the more you'll realize that this is a bit more flexible functionality in the System.Windows.Forms.ComboBox than in its System.Web.UI.DropDownList cousin. Being able to reference an object with any number of values based on what is in a combobox is, to use some street vernacular, "sick." (A good thing).

}

2 comments:

Unknown said...

Nice writeup. How do you provide a default selected item using just strings in the item collection and no code?

Gerry said...

Thanks for this write up, very helpful.