簡體   English   中英

ComboBox 以編程方式添加到 DataGridView 直到單元格失去焦點然后再次單擊后才會打開

[英]ComboBox added programmatically to DataGridView doesn't open until after the cell loses focus and then is clicked on again

在 C# WinForms 項目中,我從DataTable填充 DGV。 當用戶單擊其中一列的單元格時,我需要填充ComboBox單擊打開它。

但是,只有當相關單元格失去焦點(單擊表單上的其他位置)然后重新獲得焦點(再次單擊該單元格)時,CBO 才會打開 - 並且僅在單擊 CBO 的向下箭頭時才會打開,而不是在 CBO 的文本是點擊。 單擊 CBO 的文本時,我還需要打開 CBO。

private void dgvCategories_Click(Object sender, DataGridViewCellEventArgs e)
{
    try
    {
        // Prevent code from executing if user clicks on a cell that already has a CBO
        if (e.ColumnIndex == 5 && !(dgvCategories.Rows[e.RowIndex].Cells[e.ColumnIndex].GetType().Name == "DataGridViewComboBoxCell"))
        {
            // Get fields to build New Value query
            List<string> lsNewValuesResult = new List<string>();
            string strCategory = dtCategories.Rows[e.RowIndex][1].ToString();
            string strCompanyName = cboSelectCompany.Text;
            string strQueryGetNewValuesValidationInfo = "SELECT validationdb, validationtable, validationfield, validationfield2, validationvalue2" +
                                                    " FROM masterfiles.categories" +
                                                    " WHERE category = @category";

            // Pass validation info query to db and return list of New Values
            db getListOfNewValues = new db();
            lsNewValuesResult = getListOfNewValues.GetNewValuesList(strQueryGetNewValuesValidationInfo, strCategory, strCompanyName);

            // Create CBO object
            DataGridViewComboBoxCell cboNewValueList = new DataGridViewComboBoxCell();

            //Populate the combobox with the list of New Values
            foreach (string strListItem in lsNewValuesResult) cboNewValueList.Items.Add(strListItem);

            // Bind the CBO to the DGV
            dgvCategories[e.ColumnIndex, e.RowIndex] = cboNewValueList;

            var editingControl = dgvCategories.EditingControl as DataGridViewComboBoxEditingControl;
            if (editingControl != null) editingControl.DroppedDown = true;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("dgvCategories_Click Exception: " + ex.Message);
    }

}

DataGridViewEditMode設置為EditOnEnter並且DataGrieViewSelectionMode設置為CellSelect

最后的兩行來自 SO 問題,“ DataGridViewComboBoxColumn - 必須單擊單元格兩次才能顯示組合框

我不確定還能嘗試什么...

如果您考慮使用CellBeginEdit事件來解決您的問題,那么您可以使用下一種方法:

private void dgvCategories_CellBeginEdit(Object sender, DataGridViewCellCancelEventArgs e)
{
    if (e.ColumnIndex == 5)
    {
        if (dgvCategories.Rows[e.RowIndex].Cells[e.ColumnIndex].GetType().Name != "DataGridViewComboBoxCell")
        {
            // Bind combobox to dgv and than bind new values datasource to combobox
            DataGridViewComboBoxCell cboNewValueList = new DataGridViewComboBoxCell();

            // Get fields to build New Value query
            List<string> lsNewValuesResult = getCboValues();

            //Populate the combobox with the list of New Values
            foreach (string strListItem in lsNewValuesResult)
            {
                cboNewValueList.Items.Add(strListItem);
            }

            // Store current cell value into ComboBox cell. It is for convenience.
            cboNewValueList.Value = dgvCategories[e.ColumnIndex, e.RowIndex].Value;

            // Cancel current BeginEdit event because it occurs on the cell of type TextBox.
            // Later we'll launch another BeginEdit event on cell of type ComboBox.
            e.Cancel = true;

            // BeginInvoke is needed because:
            // 1. there is a known problem with DataGridView:
            //   - https://stackoverflow.com/questions/5114668/why-is-my-bound-datagridview-throwing-an-operation-not-valid-because-it-results
            //   - https://stackoverflow.com/questions/26522927/how-to-evade-reentrant-call-to-setcurrentcelladdresscore/26527759#26527759
            // 2. current cell has type TextBox, but we want to change its type to
            //   ComboBox and then begin editing ComboBox.
            BeginInvoke(new Action(
                () =>
                {
                    // Change type of current cell to ComboBox.
                    dgvCategories[e.ColumnIndex, e.RowIndex] = cboNewValueList;
                    // Begin editing of the ComboBox cell.
                    dgvCategories.BeginEdit(true);

                    // Here (after BeginEdit) the type of the cell is ComboBox.
                    // Automatically drop down ComboBox cell.
                    if (dgvCategories.EditingControl != null)
                        ((DataGridViewComboBoxEditingControl)dgvCategories.EditingControl).DroppedDown = true;
                }));
        }
        else
        {
            // If current cell is already of type ComboBox then we simply drop down it.
            BeginInvoke(new Action(
                () =>
                {
                    if (dgvCategories.EditingControl != null)
                        ((DataGridViewComboBoxEditingControl)dgvCategories.EditingControl).DroppedDown = true;
                }));
        }
    }
}

以下是對這種方法的解釋:

  • 如果CellBeginEdit事件發生在TextBox類型的單元格上,我們取消此事件,然后:
    • 將當前單元格類型更改為ComboBox
    • 為當前ComboBox單元啟動CellBeginEdit事件;
    • 自動下拉電流ComboBox電池;
  • 如果CellBeginEdit事件發生在ComboBox類型的單元格上,我們只需將其下拉;
  • 我們使用BeginInvoke更改當前單元格的類型,然后開始編輯,因為:
    • DataGridView ( 1 , 2 ) 存在一個已知問題,迫使我們使用BeginInvoke
    • 當事件CellBeginEdit完成時,單元格的類型會更改。

使用CellBeginEdit的方法還有下一個好處:它可以更改單元格類型,或者用戶使用鼠標或鍵盤導航到單元格。


另一種方法是使用CellStateChanged事件:

private void dgvCategories_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
{
    if (e.StateChanged == DataGridViewElementStates.Selected)
    {
        int col = e.Cell.ColumnIndex;
        int row = e.Cell.RowIndex;

        if (col == 5)
        {
            if (dgvCategories.Rows[row].Cells[col].GetType().Name != "DataGridViewComboBoxCell")
            {
                // Bind combobox to dgv and than bind new values datasource to combobox
                DataGridViewComboBoxCell cboNewValueList = new DataGridViewComboBoxCell();

                // Get fields to build New Value query
                List<string> lsNewValuesResult = getCboValues();

                //Populate the combobox with the list of New Values
                foreach (string strListItem in lsNewValuesResult)
                {
                    cboNewValueList.Items.Add(strListItem);
                }

                // 
                cboNewValueList.Value = dgvCategories[col, row].Value;
                dgvCategories[col, row] = cboNewValueList;

                // To drop down current cell we must call BeginInvoke,
                // because in the CellStateChanged event handler
                // dgvCategories.EditingControl is null.
                BeginInvoke(new Action(
                    () =>
                    {
                        if (dgvCategories.EditingControl != null)
                            ((DataGridViewComboBoxEditingControl) dgvCategories.EditingControl).DroppedDown = true;
                    }));
            }
            else
            {
                // If current cell is already ComboBox we simply drop it down.
                BeginInvoke(new Action(
                    () =>
                    {
                        if (dgvCategories.EditingControl != null)
                            ((DataGridViewComboBoxEditingControl)dgvCategories.EditingControl).DroppedDown = true;
                    }));
            }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM