Search code examples
c#winformsc#-4.0datagridviewdatagridviewcombobox

how to fill second Column according to selection of first GridViewComboBoxColumn


i have two DataGridViewComboBoxColumn. i want to Fill second Column according to selection of first GridViewComboBoxColumn.I have groups in first Combobox and services in second one. i want when i select group1, only the services of group1 shows in services combobox. i am using Stored Procedure.

var groupColumn = new DataGridViewComboBoxColumn();

                DataTable dtcategori = cb.GetAllDataCategori();
                groupColumn.Name = "Group";
                groupColumn.HeaderText = Resources.GroupName;
                groupColumn.DataSource = dtcategori;
                groupColumn.ValueMember = "ID";
                groupColumn.DisplayMember = "Name";
                groupColumn.Width = 100;
                this.DataGridViewFactor.Columns.Add(groupColumn);

                var serviceColumn = new DataGridViewComboBoxColumn();
                    //var categoriId = Convert.ToInt32(groupColumn.);
                // DataTable dtServices = sb.ServiceGetById(categoriId);
                    DataTable dtServices = sb.GetAllDataServices();
                    serviceColumn.Name = "Services";
                    serviceColumn.HeaderText = Resources.Service;
                    serviceColumn.DataSource = dtServices;
                    serviceColumn.ValueMember = "ID";
                    serviceColumn.DisplayMember = "Name";
                    serviceColumn.Width = 100;

                    this.DataGridViewFactor.Columns.Add(serviceColumn);

Solution

  • Wow, it took about 2 hours for me to find out this answer, well not good but usable and helpful :)

    First, you have to change your groupColumn.DataSource to a BindingSource not a DataTable as in your code, because you need some PositionChanged event to notify when you select an item from a combobox in groupColumn. You know that DataGridViewColumn doesn't have any SelectedIndexChanged event. Second, I don't see any relationship between your Group and Services, the datasource of Services should have a field like GroupID, so that you can link between 2 columns. I suppose there is such a GroupID field. Your Services has an ID but I'm not sure if it's GroupID, it may be your ServiceID.

    Recommended: I think you should change your groupColumn's and serviceColumn's DisplayStyle to DataGridViewComboBoxDisplayStyle.Nothing - it looks best to me.

    I would like to explain my solution in the comment beside the code. Here is the code:

    //Your code is modified a little.
    var groupColumn = new DataGridViewComboBoxColumn();
    groupColumn.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
    DataTable dtcategori = cb.GetAllDataCategori();
    groupColumn.Name = "Group";
    groupColumn.HeaderText = Resources.GroupName;
    //Create a BindingSource from your DataTable
    BindingSource bs = new BindingSource(dtcategori,"");
    groupColumn.DataSource = bs;
    bs.PositionChanged += (s,e) => {
       //Filter for items which have the selected GroupID
       ((DataTable)((DataGridViewComboBoxColumn)DataGridViewFactor.Columns["Services"]).DataSource).DefaultView.RowFilter =
                    string.Format("GroupID='{0}'",((DataRowView)bs.Current).Row["ID"]);
       //Set the initial value of the corresponding cell in serviceColumn
       DataGridViewFactor.CurrentRow.Cells["Services"].Value = ((DataTable)((DataGridViewComboBoxColumn)DataGridViewFactor.Columns["Services"]).DataSource).DefaultView.ToTable().Rows[0]["Name"];
    };
    //-------------------------------------------
    groupColumn.ValueMember = "ID";
    groupColumn.DisplayMember = "Name";
    groupColumn.Width = 100;
    this.DataGridViewFactor.Columns.Add(groupColumn);
    
    var serviceColumn = new DataGridViewComboBoxColumn();
    serviceColumn.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
    //var categoriId = Convert.ToInt32(groupColumn.);
    // DataTable dtServices = sb.ServiceGetById(categoriId);
    DataTable dtServices = sb.GetAllDataServices();
    serviceColumn.Name = "Services";
    serviceColumn.HeaderText = Resources.Service;
    serviceColumn.DataSource = dtServices;
    serviceColumn.ValueMember = "ID";
    serviceColumn.DisplayMember = "Name";
    serviceColumn.Width = 100;
    
    this.DataGridViewFactor.Columns.Add(serviceColumn);
    
    //Because filtering this way can make some cell have a value which is not contained in 
    //the DataGridViewComboBoxColumn.Items, we have to handle the DataError
    private void DataGridViewFactor_DataError(object sender, DataGridViewDataErrorEventArgs e){
        //We're interested only in DataGridViewComboBoxColumn
        if(DataGridViewFactor.Columns[e.ColumnIndex] is DataGridViewComboBoxColumn){
            e.Cancel = true;
        }
    }
    //Because when you filter on a row, the DataGridViewComboBoxColumn.DataSource with
    //the filtered DefaultView will apply on all the rows in the same 
    //DataGridViewComboBoxColumn, we have to apply the filter for each row if it is selected
    private void DataGridViewFactor_SelectionChanged(object sender, EventArgs e)
    {            
            ((DataTable)((DataGridViewComboBoxColumn)DataGridViewFactor.Columns["Services"]).DataSource).DefaultView.RowFilter =
               string.Format("GroupID='{0}'", DataGridViewFactor.CurrentRow.Cells["Group"].Value);            
    }
    //Because when you filter on a row, the DataSource DefaultView of the groupColumn will
    //be changed (limited to fewer items) and there will be cells in that column having 
    //values which are not contained in the filtered items. Those cells will not be 
    //displayed when you move the mouse over. This CellPainting event handler is to help 
    //them look normal.
    private void DataGridViewFactor_CellPainting(object sender, DataGridViewCellPaintingEventArgs e){
        if (e.RowIndex > -1 && e.ColumnIndex > -1)
        {
                if (e.Value != null)
                {
                    e.Handled = true;
                    e.PaintBackground(e.CellBounds, true);
                    StringFormat sf = new StringFormat() { LineAlignment = StringAlignment.Center };
                    e.Graphics.DrawString(e.Value.ToString(), DataGridViewFactor.Font, new SolidBrush(DataGridViewFactor.ForeColor), e.CellBounds, sf);
                }
        }
    }
    

    And that's all.

    I hope it helps you!