Подтвердить что ты не робот

Есть ли способ заставить DataGridView запускать событие CellFormatting для всех ячеек?

Мы используем событие CellFormatting для окраски кодовых ячеек в различных сетках по всему нашему приложению.

У нас есть общий код, который обрабатывает экспорт в Excel (и печать), но он делает это в черно-белом режиме. Теперь мы хотим изменить это и выбрать цвет из сеток.

Этот вопрос и ответ помог (и он работает)... за исключением проблемы с большими сетками, которые выходят за пределы одного экрана. Части сетки, которые еще не были отображены на экране, (логически) никогда не получают код CellFormatting, и поэтому их базовый цвет никогда не будет установлен. В результате в Excel цветовое кодирование выходит на половину страницы вниз.

Кажется, есть три решения:

1) Сообщите пользователю, что он должен прокручивать все части сетки перед выполнением экспорта в Excel. Ха! Не серьезное решение

2) Программируйте прокрутку ко всем частям сетки перед выполнением экспорта в Excel. Только менее ужасно, чем (1)

3) В нашем коде "Экспорт в Excel" запустите что-то вверху, которое сообщает DataGridView о том, чтобы нарисовать/форматировать всю его область, например.

  MyDataGridView.FormatAllCells()

Есть ли что-то подобное?

О, и есть четвертый вариант, но это будет касаться огромного количества существующего кода:

4) Прекратите использование события CellFormatting, отформатируйте ячейки во время загрузки. Проблема с этим заключается в том, что нам нужно будет перенастроить каждую сетку в нашем приложении, поскольку CellFormatting - это то, как мы это сделали с года.

4b9b3361

Ответ 1

Предполагая, что, как указывает @DavidHall, нет волшебства .FormatAllCells, наш единственный вариант - прекратить использование CellFormatting.

Однако новая проблема заключается в том, что применение форматирования стиля ячейки во время загрузки не оказывает никакого влияния. Много сообщений там, если вы Google это. Также они отмечают, что если вы поместите тот же код под кнопкой в ​​форме и щелкните по нему после загрузки (вместо загрузки, код будет работать... так что сетка должна быть видимой до применения стиля). Большинство советов по этой теме подсказывают, что вы используете... drumroll... CellFormatting. Aargh!

В конце концов нашел сообщение, в котором предлагается использовать событие DataBindingComplete сетки. И это работает.

По общему признанию, это решение является вариантом моей нежелательной опции "4".

Ответ 2

У меня есть возможное решение. В вашей функции экспорта вы можете получить доступ к свойству Cell.FormattedValue для каждой ячейки. Согласно Microsoft, это заставляет событие CellFormatting запускаться.

Ответ 3

У меня была такая же проблема, и у меня получилось что-то очень похожее на ваше решение №4. как вы, я использовал событие DataBindingComplete. но, поскольку я использовал метод расширения, изменения в существующем коде можно снести:

internal static class DataGridViewExtention
{
    public static void SetGridBackColorMyStyle(this DataGridView p_dgvToManipulate)
    {
        p_dgvToManipulate.RowPrePaint += p_dgvToManipulate_RowPrePaint;
        p_dgvToManipulate.DataBindingComplete += p_dgvToManipulate_DataBindingComplete;
    }

    // for the first part - Coloring the whole grid I used the `DataGridView.DataBindingComplete` event:
    private static void p_dgvToManipulate_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
    {
        foreach (DataGridViewRow objCurrRow in ((DataGridView)sender).Rows)
        {
            // Get the domain object from row 
            DomainObject objSelectedItem = (DomainObject)objCurrRow.DataBoundItem;

            // if item is valid ....
            if objSelectedItem != null)
            {
                // Change backcolor of row using my method
                objCurrRow.DefaultCellStyle.BackColor = GetColorForMyRow(objSelectedItem);
            }
        }
    }

    // for the second part (disabling the Selected row from effecting the BackColor i've setted myself, i've used `DataGridView.RowPrePaint` event:
    private static void p_dgvToManipulate_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
    {
        // If current row about to be painted is selected by user
        if (((DataGridView)sender).Rows[e.RowIndex].Selected)
        {
            // Get current grid row
            var objGridRow = ((DataGridView)sender).Rows[e.RowIndex];

            // Get selectedItem
            DomainObject objSelectedItem = (DomainObject)objGridRow.DataBoundItem;

            // if item is valid ....
            if (objSelectedItem != null && objSelectedItem.ObjectId != 0)
            {
                // Set color for row instead of "DefaultCellStyle" (same color as we used at DataBindingComplete event)
                objGridRow.DefaultCellStyle.SelectionBackColor = GetColorForMyRow(objSelectedItem);
            }

            // Since the selected row is no longer unique, we need to let the used to identify it by making the font Bold
            objGridRow.DefaultCellStyle.Font = new Font(((DataGridView)sender).Font.FontFamily, ((DataGridView)sender).Font.Size, FontStyle.Bold);
        }
        // If current row is not selected by user
        else
        {
            // Make sure the Font is not Bold. (for not misleading the user about selected row...)
            ((DataGridView)sender).Rows[e.RowIndex].DefaultCellStyle.Font = new Font(((DataGridView)sender).Font.FontFamily,
                                                                                   ((DataGridView)sender).Font.Size, FontStyle.Regular);
        }
    }
}

Ответ 4

Возможное решение, если вы хотите повторно использовать форматирование, предоставленное во время события Cellformatting (например, элементы cellstyle, такие как fontbold и backgroundcolor). Кажется, что эти клеточные стили доступны только между событиями "cellformatting" и "cellpainting", но не в самом стиле datagridview-cell.

Захват сотовых стилей во время события cellformatting со вторым обработчиком следующим образом:

  • в exportmodule добавьте общий список, массив или словарь для хранения cellstyles:

    Dim oDataGridFormattingDictionary as  Dictionary(Of String, DataGridViewCellStyle) = nothing
    
  • инициализируйте словарь и добавьте второй обработчик в datagridview в вашем файле печати или экспорта. В vb.net что-то вроде этого:

     oDataGridFormattingDictionary = New Dictionary(Of String, DataGridViewCellStyle)
     AddHandler MyDatagridviewControl.CellFormatting, AddressOf OnPrintDataGridView_CellFormatting
    
  • Добавьте код для обработчика

    Private Sub OnPrintDataGridView_CellFormatting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs)
    If e.RowIndex > -1 AndAlso e.ColumnIndex > -1 AndAlso Not e.CellStyle Is Nothing Then
       If Not oDataGridFormattingDictionary Is Nothing andalso oDataGridFormattingDictionary.ContainsKey(e.RowIndex & "_" & e.ColumnIndex) = False Then
        oDataGridFormattingDictionary.Add(e.RowIndex & "_" & e.ColumnIndex, e.CellStyle)
       End If
    End If
    End Sub
    
  • Очень важно: чтобы на самом деле было вызвано исходное событие формирования ячейки (и второй обработчик для обработки ячейки после этого), вы должны запросить formattedvalue для каждой ячейки, которую вы хотите распечатать (например,

    oValue = Datagridview.rows(printRowIndex).Cells(printColumnIndex).FormattedValue)
    

    !

  • При печати теперь вы можете проверить, имеет ли ячейка форматирование. Например:.

    if not oDataGridFormattingDictionary is nothing andalso oDataGridFormattingDictionary.ContainsKey(printRowIndex & "_" & printColumnIndex) Then
    ... the cellstyle is accesible via:
    oDataGridFormattingDictionary(printRowIndex & "_" & printColumnIndex)
    end if 
    
  • в конце файла экспорта или печати удалите обработчик и ничего не установите в словаре

    RemoveHandler DirectCast(itemToPrint.TheControl, DataGridView).CellFormatting, AddressOf OnPrintDataGridView_CellFormatting
    oDataGridFormattingDictionary = nothing
    

Ответ 5

Как отмечалось в других ответах, доступ к DataGridViewCell.FormattedValue действительно является простым способом принудительно вызвать событие CellFormatting (повторно) для конкретной ячейки. В моем случае, однако, это свойство также приводило к нежелательным побочным эффектам, связанным с автоматическим изменением размеров столбцов. При поиске долгое время для работоспособного решения я наконец встретил следующие магические методы, которые отлично работают: DataGridView.Invalidate(), DataGridView.InvalidateColumn(), DataGridView.InvalidateRow() и DataGridView.InvalidateCell().

Эти 4 метода заставляют событие CellFormatting переименовываться только для указанной области (ячейка, столбец, строка или целая таблица), а также без каких-либо неприятных артефактов для автоматического изменения размера.