Datagridview comboboxcolumn different values for each row

13.4k views Asked by At

I want to create a datagridview with comboboxcolumns using c#.

The problem is that I dont know how to give different values for combobox in each row.

DataTable dt = new DataTable();
dt.Columns.Add("state");
dt.Columns.Add("city");
dt.Rows.Add("a1", "b1");
dt.Rows.Add("a1", "b2");
dt.Rows.Add("a2", "b3");
dt.Rows.Add("a2", "b4");
dt.Rows.Add("a3", "b5");
DataGridViewComboBoxColumn comboStates = new DataGridViewComboBoxColumn();
comboStates.HeaderText = "HeaderText_1";
this.dataGridView1.Columns.Insert(0, comboStates);
DataGridViewComboBoxColumn comboCities = new DataGridViewComboBoxColumn();
comboCities.HeaderText = "HeaderText_2";
this.dataGridView1.Columns.Insert(1, comboCities);

for (int i = 0; i < dt.Rows.Count; i++)
{
    dataGridView1.Rows.Add();
    comboStates.Items.Add(dt.Rows[i][0]);
        DataGridViewComboBoxCell stateCell = (DataGridViewComboBoxCell)    (dataGridView1.Rows[i].Cells[0]);
    stateCell.Value = comboStates.Items[i];
    comboCities.Items.Add(dt.Rows[i][1]);
    DataGridViewComboBoxCell cityCell = (DataGridViewComboBoxCell)(dataGridView1.Rows[i].Cells[1]);
    cityCell.Value = comboCities.Items[i];
}

This example gives the following results :

for each row:

comboboxcolumnstate :

a1
a1
a2
a2
a3

comboboxcolumncity :

b1
b2
b3
b4
b5

I know this is normal because I am looping through datatable.

So how can i get this result:

row1: comboboxcolumnstate comboboxcolumncity

             a1               b1 - b2

row2: comboboxcolumnstate comboboxcolumncity

             a2               b3 - b4

row2: comboboxcolumnstate comboboxcolumncity

             a3               b5

I am new to C#. I searched a lot but I didn't find a solution which solve this issue. Thanks

1

There are 1 answers

0
TaW On BEST ANSWER

Here is an example code that should get you started.

First I create a list of values (cities) for each state. For this the Dictionary collection comes handy:

Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();

for (int i = 0; i < dt.Rows.Count; i++)
{
    string state = dt.Rows[i][0].ToString();
    string city  = dt.Rows[i][1].ToString();

    if (! dict.Keys.Contains(state ))  dict.Add(state, new List<string>());
    dict[state].Add(city);
}

I loop over your table and for each row I add a new entry for the city and, if necessary, for the state.

Now we modify the setup of the DGV to use those Lists in the Items of each of the DataGridViewComboBoxCell.

Note: Here the key is to use the DataGridViewComboBoxCells, and not the DataGridViewComboBoxColumns!

For easier access I create string variables for the state and city values again..

for (int i = 0; i < dt.Rows.Count; i++)
{
    string state = dt.Rows[i][0].ToString();
    string city = dt.Rows[i][1].ToString();
    dataGridView1.Rows.Add();
    DataGridViewComboBoxCell stateCell = 
                            (DataGridViewComboBoxCell)(dataGridView1.Rows[i].Cells[0]);
    stateCell.Items.AddRange(dict.Keys.ToArray());
    stateCell.Value = state;
    DataGridViewComboBoxCell cityCell = 
                            (DataGridViewComboBoxCell)(dataGridView1.Rows[i].Cells[1]);
    cityCell.Items.AddRange(dict[state].ToArray());
    cityCell.Value = city;
}

Note: This sets up the values in the DropDowns, but after each change in a 'State' column you will need to adapt the 'Cities' column in that row! Therefore it would be a good idea to make the Dictionary persistent by moving it to class level and to create a little function you can call after such a change.

Here is an example of this functionality:

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == 0)
        setCities(e.RowIndex, dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString());
}

void setCities(int row, string state)
{
    DataGridViewComboBoxCell cityCell = 
                           (DataGridViewComboBoxCell)(dataGridView1.Rows[row].Cells[1]);
    cityCell.Items.Clear();
    cityCell.Items.AddRange(dict[state].ToArray());
    cityCell.Value = cityCell.Items[0];
}

Note that the new cityCell.Value shows up only after leaving the edited cell!

Final note: If you really want to display only one row per state, you need to change the loop that fills the DGV to loop not over your table but over the dict.Keys collection:

for (int i = 0; i <  dict.Keys.Count; i++)
{
    string state = dict.Keys.ElementAt(i);
    string city1 = dict[state][0].ToString();
    dataGridView1.Rows.Add();
    DataGridViewComboBoxCell stateCell = 
                            (DataGridViewComboBoxCell)(dataGridView1.Rows[i].Cells[0]);
    stateCell.Items.AddRange(dict.Keys.ToArray());
    stateCell.Value = state;
    DataGridViewComboBoxCell cityCell = 
                            (DataGridViewComboBoxCell)(dataGridView1.Rows[i].Cells[1]);
    cityCell.Items.AddRange(dict[state].ToArray());
    cityCell.Value = city1;
}