Populating ComboBox from Database: Proper Use of Data Binding and DisplayMember/ValueMember

Dec 06, 2025 · Programming · 7 views · 7.8

Keywords: ComboBox | Data Binding | DisplayMember | ValueMember | C#

Abstract: This article discusses common errors in setting DisplayMember and ValueMember when populating a ComboBox from a database in C#. By analyzing a typical code example, it explains why setting these properties within a loop causes issues and provides a solution based on DataTable data binding. The article details methods using SqlDataAdapter and DataSet, including connection management, exception handling, and the use of the SelectedIndexChanged event. Additionally, it briefly compares the performance differences between DataReader and DataTable, and supplements with alternative approaches using custom classes or anonymous types.

Problem Background and Common Errors

In C# application development, dynamically populating a ComboBox from a database is a common requirement. However, many developers fall into pitfalls when using the DisplayMember and ValueMember properties. For example, the following code snippet illustrates a typical mistake:

SqlConnection conn = new SqlConnection();
try
{
    conn = new SqlConnection(@"Data Source=SHARKAWY;Initial Catalog=Booking;Persist Security Info=True;User ID=sa;Password=123456");
    string query = "select FleetName, FleetID from fleets";
    SqlCommand cmd = new SqlCommand(query, conn);
    cmd.CommandText = query;
    conn.Open();
    SqlDataReader drd = cmd.ExecuteReader();
    while (drd.Read())
    {
         cmbTripName.Items.Add(drd["FleetName"].ToString());
         cmbTripName.ValueMember = drd["FleetID"].ToString();
         cmbTripName.DisplayMember = drd["FleetName"].ToString();
    }
}
catch
{
     MessageBox.Show("Error ");
}

The issue with this code is that DisplayMember and ValueMember are incorrectly set to specific data values rather than column names. This leads to problems in subsequent operations, such as when trying to retrieve the current selected item via the SelectedIndexChanged event, where accessing the values of FleetName and FleetID fails. For instance, in a button click event:

private void button1_Click(object sender, EventArgs e)
{
    label1.Text = cmbTripName.DisplayMember;
    label2.Text = cmbTripName.ValueMember;
}

The output is merely the strings "FleetName" and "FleetID", not the actual data values.

Correct Solution: Using DataTable for Data Binding

To address the above issue, it is recommended to use a DataTable as the data source and specify column names via DisplayMember and ValueMember. The following code demonstrates the improved implementation:

using (SqlConnection conn = new SqlConnection(@"Data Source=SHARKAWY;Initial Catalog=Booking;Persist Security Info=True;User ID=sa;Password=123456"))
{
    try
    {
        string query = "select FleetName, FleetID from fleets";
        SqlDataAdapter da = new SqlDataAdapter(query, conn);
        conn.Open();
        DataSet ds = new DataSet();
        da.Fill(ds, "Fleet");
        cmbTripName.DisplayMember =  "FleetName";
        cmbTripName.ValueMember = "FleetID";
        cmbTripName.DataSource = ds.Tables["Fleet"];
    }
    catch (Exception ex)
    {
        // Write exception information to a log or handle appropriately
        MessageBox.Show("Error occurred!");
    }               
}

The key advantages of this approach include:

Handling Selected Item Change Events

To retrieve corresponding values when the user selects different items, handle the SelectedIndexChanged event:

private void cmbTripName_SelectedIndexChanged(object sender, EventArgs e)
{
    if (cmbTripName.SelectedItem != null)
    {
        DataRowView drv = cmbTripName.SelectedItem as DataRowView;

        Debug.WriteLine("Item: " + drv.Row["FleetName"].ToString());
        Debug.WriteLine("Value: " + drv.Row["FleetID"].ToString());
        Debug.WriteLine("Value: " + cmbTripName.SelectedValue.ToString());
    }
}

Here, SelectedItem is cast to a DataRowView, allowing direct access to the FleetName and FleetID column values. Additionally, the SelectedValue property provides a convenient alternative.

Performance Considerations and Alternatives

Using a DataTable may be slightly slower than using a DataReader because it caches the entire result set in memory. However, this difference is negligible in most application scenarios, and DataTable offers a simpler data binding mechanism. For performance-critical applications, consider using DataReader with a custom class:

while (drd.Read())
{
    cmbTripName.Items.Add(new Fleet(drd["FleetID"].ToString(), drd["FleetName"].ToString()));
}
cmbTripName.ValueMember = "FleetId";
cmbTripName.DisplayMember = "FleetName";

Where the Fleet class is defined as:

class Fleet
{
     public Fleet(string fleetId, string fleetName)
     {
           FleetId = fleetId;
           FleetName = fleetName;
     }
     public string FleetId {get;set;}
     public string FleetName {get;set;}
}

Or simplify the code using an anonymous type:

while (drd.Read())
{
    cmbTripName.Items.Add(new {FleetId = drd["FleetID"].ToString(), FleetName = drd["FleetName"].ToString()});
}
cmbTripName.ValueMember = "FleetId";
cmbTripName.DisplayMember = "FleetName";

These methods avoid the overhead of DataTable but require additional class definitions or rely on the internal implementation of anonymous types.

Conclusion

When populating a ComboBox from a database, correctly using DisplayMember and ValueMember is crucial. Through data binding mechanisms, code can be simplified and maintainability improved. The DataTable method recommended in this article is the optimal choice in most cases, balancing ease of use with adequate performance. Developers should make reasonable trade-offs based on specific needs in areas such as data binding, resource management, and exception handling.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.