【问题标题】:Is there a way to force a DataGridView to fire its CellFormatting event for all cells?有没有办法强制 DataGridView 为所有单元格触发其 CellFormatting 事件?
【发布时间】:2012-05-18 11:21:19
【问题描述】:

我们使用 CellFormatting 事件对应用程序中各种网格中的单元格进行颜色编码。

我们有一些通用代码可以处理导出到 Excel(和打印),但它以黑白方式执行。现在我们要更改它并从网格中获取颜色。

这个question & answer 有帮助(并且有效)...除了超出单个屏幕的较大网格存在问题。尚未在屏幕上显示的网格部分(逻辑上)永远不会触发它们的 CellFormatting 代码,因此它们的底层颜色永远不会被设置。因此,在 Excel 中,颜色编码在页面的一半处消失

似乎有三种解决方案:

1) 告诉用户他必须在导出到 Excel 之前滚动到网格的所有部分。 哈!不是一个严肃的解决方案

2) 在导出到 Excel 之前,以编程方式滚动到网格的所有部分。 仅比 (1) 稍微不那么可怕

3) 在我们的导出到 Excel 代码中,在顶部触发一些东西,告诉 DataGridView 绘制/格式化其整个区域,例如

  MyDataGridView.FormatAllCells()

有这样的东西吗???

哦,还有第四个选项,但这将涉及大量现有代码:

4) 停止使用 CellFormatting 事件,在加载时格式化单元格。问题是我们必须重新调整应用程序中的每个网格,因为 CellFormatting 是我们从 dot 开始的方式。

【问题讨论】:

  • 我有一种不好的感觉,选项 3 没有解决方案。我可以建议的是选项 5:您目前拥有设置颜色和其他样式的格式化逻辑基于单元格值的属性。如果将该逻辑设为新方法,则可以从单元格格式和 Excel 代码中调用此方法。这样,您可以确保 excel 和单元格格式始终相同,但不再依赖已为您的 excel 导出调用的单元格格式。
  • 另一种选择(4 的变体)是使用 CellValueChanged 进行格式化。

标签: winforms datagridview cell-formatting


【解决方案1】:

我有一个可能的解决方案 - 在您的导出函数中访问每个单元格的 Cell.FormattedValue 属性。根据Microsoft,这会强制触发 CellFormatting 事件。

【讨论】:

  • 实际上,这听起来是个中肯的建议
  • 谢谢。 Dim strValue As StringFor r As Integer = 0 To dgv.Rows.Count - 1For c As Integer = 0 To dgv.Columns.Count - 1strValue = dgv.Rows(r).Cells(c).FormattedValue()NextNext
【解决方案2】:

正如其他答案中所述,访问DataGridViewCell.FormattedValue 确实是一种简单的方法来强制(重新)调用特定单元格的CellFormatting 事件。然而,在我的例子中,这个属性也导致了涉及自动调整列大小的不良副作用。在找了半天可行的解决方案的过程中,我终于遇到了以下完美的神奇方法:DataGridView.Invalidate()DataGridView.InvalidateColumn()DataGridView.InvalidateRow()DataGridView.InvalidateCell().

这 4 种方法强制仅针对指定范围(单元格、列、行或整个表)重新调用 CellFormatting 事件,并且不会导致任何令人讨厌的自动调整大小工件。

【讨论】:

    【解决方案3】:

    假设,正如@DavidHall 建议的那样,.FormatAllCells 没有魔法,我们唯一的选择是停止使用 CellFormatting。

    但是,这里的新问题是在加载期间应用单元格样式格式似乎没有任何效果。如果你谷歌它,那里有很多帖子。他们还指出,如果您将相同的代码放在表单上的按钮下并在加载后单击它(而不是在加载中,代码将起作用......因此网格必须在样式应用之前可见)。关于该主题的大多数建议都建议您使用 ...drumroll ... CellFormatting。啊!

    终于找到了一个帖子,建议使用网格的DataBindingComplete 事件。这行得通。

    诚然,这个解决方案是我不想要的选项“4”的变体。

    【讨论】:

      【解决方案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);
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果您确实想重用在 Cellformatting 事件期间提供的格式(例如,像 fontbold 和 backgroundcolor 这样的 cellstyle 元素),这是一个可能的解决方案。这些单元格样式似乎只在“cellformatting”和“cellpainting”事件之间可用,但在 datagridview-cell 的样式本身中不可用..

        在单元格格式化事件期间使用第二个处理程序捕获单元格样式,如下所示:

        1. 在导出模块中添加一个共享列表、数组或字典来存储单元格样式:

          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. 非常重要:要确保实际调用原始单元格式化事件(以及之后的第二个单元格式化处理程序),您必须为每个单元请求 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. 在导出或打印代码结束时删除处理程序并将字典设置为空

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多