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

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

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

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

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

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

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

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

  MyDataGridView.FormatAllCells()

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

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

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

5 ответов


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


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

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

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

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


у меня была та же проблема, и я закончил с чем-то очень похожим на ваше решение #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);
        }
    }
}

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

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


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

захват cellstyles во время cellformatting-события со вторым обработчиком, как это:

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

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

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

    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
    
  4. очень важно: чтобы убедиться, что исходное cellformating-событие (и второй cellformatting-обработчик после этого) на самом деле позвонил, вы должны запросить formattedvalue для каждой ячейки, которые вы хотите напечатать (например,

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

    !

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

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

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