C# DataGridViewComboBoxColumn Индекс вне диапазона при установке HeaderText в файле Дизайнера

Вопрос или проблема

У меня есть DataGridView на форме, в котором есть колонка ComboBox.

При загрузке формы для грида нет источника данных, так как он заполняется другим методом, вызываемым пользователем.

Когда форма загружается (создается экземпляр формы), я получаю ошибку:

Индекс находится вне диапазона. Должен быть неотрицательным и меньше размера коллекции.

Я выяснил, что проблема заключается в строке в файле конструктора формы, когда устанавливается текст заголовка для колонки. Это строка кода, вызывающая ошибку в файле Form1.Designer.cs:

this.gridComboBox1.HeaderText = "Options";

Если я закомментирую эту строку, форма загружается без проблем, но заголовок колонки комбобокса в гриде тогда просто “gridComboBox1“, что явно не идеально.

Я использую Visual Studio для создания своих форм, поэтому попробовал просто добавить совершенно новый элемент управления datagridview в приложение из панели инструментов и настроить колонки, не меняя никаких настроек, кроме типа колонки и текста заголовка. Но я получаю ту же проблему, что наводит меня на мысль, что это общая проблема с winforms, так как она предлагает функциональность, которая не работает.

Я также получаю ту же ошибку, если пытаюсь программно изменить текст заголовка в событии загрузки формы.

Я все еще довольно нов в C# и не смог выяснить, почему это происходит. Сама колонка добавляется ранее в код в файле конструктора следующим образом:

this.dgv1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
            this.select,
            this.gridComboBox1,
            this.DataType});

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

Может кто-то прояснить, почему это происходит, так как для меня это не имеет смысла.

Я провел еще некоторые исследования и, похоже, исправил проблему.

Я подписался на событие CellValueChanged, чтобы запустить код, который заполняет другую колонку в гриде в зависимости от выбранного варианта из колонки ComboBox.

Тем не менее, это событие вызывается, когда текст заголовка устанавливается кодом конструктора.

Вот код, который вызывает вопрос:

private void dgv1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    try
    {
        if (e.ColumnIndex == 1) // индекс колонки комбобокса
        {
            string mappedField = dgv1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
            string sql = @$"SELECT 
                            UPPER(DATA_TYPE) 
                            + CASE 
                                WHEN DATA_TYPE IN ('nvarchar', 'varchar') AND CHARACTER_MAXIMUM_LENGTH = -1 THEN '(MAX)' 
                                WHEN DATA_TYPE IN ('nvarchar', 'varchar') THEN '('+CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) + ')' 
                                ELSE '' 
                                END AS DATA_TYPE 
                        FROM INFORMATION_SCHEMA.COLUMNS 
                        WHERE TABLE_NAME = 'Report'
                        AND COLUMN_NAME = '{mappedField}';";
            string targetDataType = clsRepeatableMethods.SQLOutputToStringVariable(sql, clsConnections.ConnStrStg);

            dgv1.Rows[e.RowIndex].Cells["RepDataType"].Value = targetDataType;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Поскольку в гриде нет строк, кроме заголовка, когда грид создается конструкцией конструктора, возникает ошибка “индекс вне диапазона”.

Исправление заключалось в добавлении проверки для индекса строки.

Я изменил эту строку кода:

if (e.ColumnIndex == 1)

На следующую:

if (e.RowIndex != -1 && e.ColumnIndex == 1)

Теперь все работает. Как я уже говорил ранее, я новичок в C#, и не думал, что событие CellValueChanged будет срабатывать из-за изменения в строке заголовка.

Спасибо @Sinatr, чьи комментарии заставили меня взглянуть шире на то, какой другой код мог быть вызван.

Ответ или решение

Проблема, с которой вы столкнулись, связана с тем, что событие CellValueChanged для вашего DataGridView вызывается и при изменении заголовка колонки. Когда ваше приложение загружается, в DataGridView еще нет строк, кроме заголовка, и это приводит к ошибке "Индекс вне диапазона", когда вы пытаетесь получить доступ к e.RowIndex.

Ваша первоначальная логика проверки в обработчике события была недостаточной, что и вызывало проблему. Как вы правильно заметили, необходимо добавить дополнительную проверку для e.RowIndex, чтобы убедиться, что она не равна -1, прежде чем производить какие-либо операции с элементами строк.

Вот корректный фрагмент кода:

private void dgv1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    try
    {
        if (e.RowIndex != -1 && e.ColumnIndex == 1) // Проверяем индекс строки и столбца
        {
            string mappedField = dgv1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
            string sql = @$"SELECT 
                            UPPER(DATA_TYPE) 
                            + CASE 
                                WHEN DATA_TYPE IN ('nvarchar', 'varchar') AND CHARACTER_MAXIMUM_LENGTH = -1 THEN '(MAX)' 
                                WHEN DATA_TYPE IN ('nvarchar', 'varchar') THEN '('+CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10)) + ')' 
                                ELSE '' 
                                END AS DATA_TYPE 
                        FROM INFORMATION_SCHEMA.COLUMNS 
                        WHERE TABLE_NAME = 'Report'
                        AND COLUMN_NAME = '{mappedField}';";
            string targetDataType = clsRepeatableMethods.SQLOutputToStringVariable(sql, clsConnections.ConnStrStg);

            dgv1.Rows[e.RowIndex].Cells["RepDataType"].Value = targetDataType;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Теперь ваше приложение должно работать должным образом без возникновения ошибок, связанных с индексами. Также важно помнить, что при работе с событиями, такими как CellValueChanged, всегда стоит учитывать возможность случайного вызова обработчика при изменениях, не касающихся ценностей в строках данных, как в данном случае.

Если вы будете следовать этой практике проверки индексов в других подобных сценариях, это поможет избежать подобных ошибок в будущем.

Рекомендуется также изучить другие события DataGridView, которые могут работать аналогично, чтобы иметь возможность предвидеть и обрабатывать возможные ошибки на ранних этапах разработки.

Оцените материал
Добавить комментарий

Капча загружается...