ComboBox добавлен программно в ячейку DataGridView, не расширяясь при щелчке ячейки - PullRequest
2 голосов
/ 10 апреля 2020

У меня есть DataGridView в C# проекте WinForms, в котором, когда пользователь нажимает на определенные ячейки DGV, ячейка изменяется на DataGridViewComboBoxCell и ComboBox заполняется некоторыми значениями для выбора пользователем. Вот код формы для события DataGridView_Click:

private void dgvCategories_Click(Object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == 5 && !(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 = 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";
                                                //" WHERE category = '" + strCategory + "'";

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

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

        // 
        dgvCategories[e.ColumnIndex, e.RowIndex] = cboNewValueList;

    }
}

Вот код в классе db, который заполняет ComboBox (его, вероятно, нет необходимости включать в целях этого вопроса, но ради полнота, я включаю ее, на случай, если она уместна):

public List<string> GetNewValuesList(string strValidationInfoQuery, string strCategory, string strCompanyName)
{
    List<string> lsValidationInfo = new List<string>();
    List<string> lsNewValuesList = new List<string>();

    using (NpgsqlConnection conn = new NpgsqlConnection(connString))
    using (NpgsqlCommand cmd = new NpgsqlCommand(strValidationInfoQuery, conn))
    {
        cmd.Parameters.AddWithValue("category", strCategory);

        conn.Open();

        using (NpgsqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int intReaderIndex;
                for (intReaderIndex = 0; intReaderIndex <= reader.FieldCount - 1; intReaderIndex++)
                {

                    // reader indexes 3 & 4 correspond to categories.validationfield2 and validationvalue2, which can be null
                    if (string.IsNullOrEmpty(reader[intReaderIndex].ToString()))
                    {
                        lsValidationInfo.Add("");
                    }
                    else
                    {
                        lsValidationInfo.Add(reader.GetString(intReaderIndex));
                    }
                    //Console.WriteLine("reader index " + intReaderIndex + ": " + reader.GetString(intReaderIndex));
                }
            }
        }
    }

    string strValidationDb = lsValidationInfo[0];
    string strValidationTable = lsValidationInfo[1];
    string strValidationField = lsValidationInfo[2];
    string strValidationField2 = lsValidationInfo[3];
    string strValidationValue2 = lsValidationInfo[4];

    string strQueryGetNewValues = "SELECT DISTINCT " + strValidationField +
                        " FROM " + strValidationDb + "." + strValidationTable +
                        " WHERE company_id = (SELECT id FROM company WHERE name = '" + strCompanyName + "')";

    if (!string.IsNullOrEmpty(strValidationField2) && !string.IsNullOrEmpty(strValidationValue2)) strQueryGetNewValues += " AND " + strValidationField2 + " = '" + strValidationValue2 + "'";

    strQueryGetNewValues += " ORDER BY " + strValidationField;

    using (NpgsqlConnection conn = new NpgsqlConnection(connString))
    using (NpgsqlCommand cmd = new NpgsqlCommand(strQueryGetNewValues, conn))
    {
        conn.Open();

        using (NpgsqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int intReaderIndex;
                for (intReaderIndex = 0; intReaderIndex <= reader.FieldCount - 1; intReaderIndex++)
                {
                    // reader indexes 3 & 4 correspond to categories.validationfield2 and validationvalue2, which can be null
                    if (string.IsNullOrEmpty(reader[intReaderIndex].ToString()))
                    {
                        lsNewValuesList.Add("");
                    }
                    else
                    {
                        lsNewValuesList.Add(reader.GetString(intReaderIndex));
                    }
                    Console.WriteLine("reader index " + intReaderIndex + ": " + reader.GetString(intReaderIndex));
                }
            }
        }
    }

    return lsNewValuesList;
}

Заполняется поле со списком, так как я могу получить доступ к элементам в lsNewValuesResult в методе _Click. Режим редактирования DGV установлен на EditOnEnter. Я попытался EditOnKeystroke, но это не привело к расширению поля со списком при щелчке мыши.

Так выглядит поле со списком, когда на ячейку нажимают, а CBO заполняется и добавляется в ячейку DGV. :

enter image description here

Это после того, как я нажал на каждую из двух ячеек.

[РЕШЕНО]

См. Мой ответ ниже.

К сожалению, решение этой проблемы выявило новую проблему .

Ответы [ 5 ]

3 голосов
/ 15 апреля 2020

Если я правильно распознаю вашу проблему, в моем тестовом приложении я добавлю DataGridView с 6 столбцами, EditMode = EditOnEnter (другим нужно три раза щелкнуть, чтобы открыть раскрывающийся список, Насколько я пытался ) и handle CellStateChanged envent.

private void dgvCategories_CellStateChanged(object sender, DataGridViewCellStateChangedEventArgs e)
{
    if (e.StateChanged == DataGridViewElementStates.Selected)
    {
        DataGridViewCell cell = e.Cell;
        int columnIndex = cell.ColumnIndex;
        int rowIndex = cell.RowIndex;
        //---IF CONDITIONS--
        //columnIndex == 5
        //          Only cells in Columns[5]
        //cell.Selected
        //          Because this event raised two time, first for last selected cell and once again
        //          for currently selected cell and we need only currently selected cell.
        //cell.EditType.Name != "DataGridViewComboBoxEditingControl"
        //          If this cell "CellStateChanged" raised for second time, only other cell types allowed
        //          to edit, otherwise the current cell lost last selected item.
        if (columnIndex == 5 && cell.Selected && cell.EditType.Name != "DataGridViewComboBoxEditingControl")
        {
            DataGridViewComboBoxCell cboNewValueList = new DataGridViewComboBoxCell();

            //Add items to DataGridViewComboBoxCell for test, replace it with yours.
            for (int i = 0; i < 10; i++)
                cboNewValueList.Items.Add($"Item {i}");

            dgvCategories[columnIndex, rowIndex] = cboNewValueList;
        }
    }
}

ПРИМЕЧАНИЕ: пользователь должен щелкнуть два раза в ячейке, чтобы открыть раскрывающееся меню.

Edit One: Как Реза Агаи предлагает для одного клика в ячейке:

private void dgvCategories_CellClick(object sender, DataGridViewCellEventArgs e)
{
    DataGridViewComboBoxEditingControl editingControl = dgvCategories.EditingControl as DataGridViewComboBoxEditingControl;
    if (editingControl != null)
        editingControl.DroppedDown = true;
}
2 голосов
/ 15 апреля 2020

Я собираюсь публично признать, что я глуп:

По причинам дизайна и функциональности, которые требуются для этого проекта, я вручную устанавливаю ширину и имена столбцов DGV, и я также нужны 2-4 столбцы ReadOnly = true. Ну, я по неосторожности установил 5-й столбец - столбец, к которому этот вопрос также относится ReadOnly = true.

Спасибо всем за ваши попытки ответить. Это просто напоминает нам о том, что что-то настолько простое может вызвать, казалось бы, большую проблему и так легко упустить из виду!

0 голосов
/ 15 апреля 2020

Вы можете учитывать следующие факты о DataGridView:

  • Если вы установите AutoGenerateColumns на false, то вам нужно добавить столбцы в коллекцию Columns вручную .
  • Если установить AutoGenerateColumns на true, когда вы назначаете данные для DataSource, элемент управления автоматически создает столбцы для источника данных. В этом случае элемент управления просматривает список столбцов источника данных, и для каждого столбца, если в коллекции Columns элемента управления, имеющего тот же DataPropertyName, что и имя столбца источника данных, нет столбца, он добавит столбец в Columns collection.
  • DataPropertyName столбцов datagridviews определяет связанный столбец источника данных.
  • Обычно вы хотите добавить DataGridViewXXXXColumn к столбцам сбора вместо использования DataGridViewXXXXCell для ячейки.
  • Если вы установите EditMode на EditOnEnter, то, если вы нажмете на кнопку выпадающего меню, достаточно одного щелчка. Если вы нажмете на содержимое ячейки, потребуется два щелчка.
  • Если вы хотите сделать его одним кликом, даже если вы нажмете на содержимое ячейки, взгляните на это сообщение . (Примечание: я не использовал этот пример, он немного раздражает.)

  • вы можете установить DisplayStyle на Nothing , затем он показывает столбец как поле со списком, просто в режиме редактирования.

Базовый c пример использования DataGridViewComboBoxColumn

Я полагаю, что вы собираетесь показать список продуктов с (Id, Name, Price, CategoryId) в DataGridView, а CategoryId должен быть из списка категорий, имеющих (Id, Name), и вы собираетесь показать CategoryId как ComboBox.

enter image description here

На самом деле это базовый c и classi c пример DataGridViewComboBoxColumn:

private void Form1_Load(object sender, EventArgs e) {
    var categories = GetCategories();
    var products = GetProducts();
    var idColumn = new DataGridViewTextBoxColumn() {
      Name = "Id", HeaderText = "Id", DataPropertyName = "Id"
    };
    var nameColumn = new DataGridViewTextBoxColumn() {
      Name = "Name", HeaderText = "Name", DataPropertyName = "Name"
    };
    var priceColumn = new DataGridViewTextBoxColumn() {
      Name = "Price", HeaderText = "Price", DataPropertyName = "Price"
    };
    var categoryIdColumn = new DataGridViewComboBoxColumn() {
      Name = "CategoryId", HeaderText = "Category Id", DataPropertyName = "CategoryId",
      DataSource = categories, DisplayMember = "Name", ValueMember = "Id",
      DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing
    };
    dataGridView1.Columns.AddRange(idColumn, nameColumn, priceColumn, categoryIdColumn);
    dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
    dataGridView1.AutoGenerateColumns = false;
    dataGridView1.DataSource = products;
}
public DataTable GetProducts() {
    var products = new DataTable();
    products.Columns.Add("Id", typeof(int));
    products.Columns.Add("Name", typeof(string));
    products.Columns.Add("Price", typeof(int));
    products.Columns.Add("CategoryId", typeof(int));
    products.Rows.Add(1, "Product 1", 100, 1);
    products.Rows.Add(2, "Product 2", 200, 2);
    return products;
}
public DataTable GetCategories() {
    var categories = new DataTable();
    categories.Columns.Add("Id", typeof(int));
    categories.Columns.Add("Name", typeof(string));
    categories.Rows.Add(1, "Category 1");
    categories.Rows.Add(2, "Category 2");
    return categories;
}

Подробнее

Чтобы узнать больше о DataGridView, взгляните на DataGridView Control (Windows Forms) . Он содержит ссылки на некоторые документы и полезные статьи с инструкциями, в том числе:

0 голосов
/ 15 апреля 2020

Возможно, вы получаете сообщение об ошибке, которое по какой-то причине не отображается?

Если я использую ваш код, DataGridViewComboBoxCell кажется заполненным значениями, но я получаю DataGridViewComboBoxCell value is not valid ошибка времени выполнения.

Этот тестовый код работает нормально для меня:

private void dgvCategories_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    DataGridViewComboBoxCell cboNewValueList = new DataGridViewComboBoxCell();

    List<string> lsNewValuesResult = new List<string>();
    lsNewValuesResult.Add("Value1");
    lsNewValuesResult.Add("Value2");
    lsNewValuesResult.Add("Value3");

    foreach (string strListItem in lsNewValuesResult)
    {
        cboNewValueList.Items.Add(strListItem);
    }

    dgvCategories[e.ColumnIndex, e.RowIndex] = cboNewValueList;

    // Added setting of initial value
    cboNewValueList.Value = cboNewValueList.Items[0];   
}

Поэтому, возможно, попробуйте установить начальное значение для DataGridViewComboBoxCell после добавления его в DataGridView.

0 голосов
/ 15 апреля 2020

Возможно, вам придется отключить AutoGenerateColumns:

Кроме того, кажется, что для раскрывающегося списка требуется три щелчка.

    public Form1()
    {
        InitializeComponent();
        dataGridView1.AutoGenerateColumns = false;
        dataGridView1.DataSource = GetDataSource();
        DataGridViewComboBoxColumn dgvcbc = new DataGridViewComboBoxColumn();
        dgvcbc.Items.Add("R0C0");
        dgvcbc.Items.Add("R1C0");
        dgvcbc.Items.Add("R2C0");
        dgvcbc.Items.Add("R3C0");
        dgvcbc.DataPropertyName = "Col0";
        dataGridView1.Columns.Add(dgvcbc);
    }

    DataTable GetDataSource()
    {
        var dtb = new DataTable();
        dtb.Columns.Add("Col0", typeof(string));
        dtb.Columns.Add("Col1", typeof(string));
        dtb.Columns.Add("Col2", typeof(string));
        dtb.Columns.Add("Col3", typeof(string));
        dtb.Columns.Add("Col4", typeof(string));
        dtb.Rows.Add("R0C0", "R0C1", "R0C2", "R0C3", "R0C4");
        dtb.Rows.Add("R1C0", "R1C1", "R1C2", "R1C3", "R1C4");
        dtb.Rows.Add("R2C0", "R2C1", "R2C2", "R2C3", "R2C4");
        dtb.Rows.Add("R3C0", "R3C1", "R3C2", "R3C3", "R3C4");
        return dtb;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...