繁体   English   中英

基于单元格值的颜色 Datagridview 单元格背景颜色(阴影像 excel 条件格式)vb.net

[英]Colour Datagridview cells backcolor based on cell value (shaded like excel conditional formatting) vb.net

在 datagridview 中,我想为其中一个列的背景颜色着色,称为“COST”,最高值为红色,最低值为绿色。 之间的每个其他值都需要是红色到黄色到绿色的阴影。

我在 .NET 中发现了Color Interpolation Between 3 Colors但这对我来说有点复杂,即使转换回 VB.net 我也不知道我需要插入 function 是什么,即使它会按照我想要的方式工作。

有没有我写或添加的简单的 class 或 function 将采用最大值、最小值和阴影之间的所有值。 我不介意它是否必须遍历所有行。

我当前的数据有 345 行,最大值为 1673.86,最小值为 4.99 数据来自数据源,用户无法更改单元格值。 我也在VB.net工作

好吧......即使在设置之后,我仍然不确定这“如何”帮助用户。 但是,质疑这些事情不是我的事。 我相信有很多方法可以做到这一点,下面是一种可能适合您需求的 hacky 方法。

在下面的方法中,我基本上创建了一个方法,该方法采用单元格值并根据您描述的要求返回Color 在下面的示例中,我创建了一个循环遍历网格中所有行的方法,并使用前面的方法设置单元格颜色。 您“可以”将此代码放在网格的单元格或行“绘制”方法之一中,但这似乎有点矫枉过正,因为正如您评论的那样…… “用户无法更改单元格值。” … 因此,如果用户不能更改任何单元格值,那么我们只需要在加载或重新加载数据时或者在对网格进行排序或过滤时调用我们的方法。

最后,此解决方案非常特定于已发布的要求。 换句话说,根据使用的 colors 和数据范围,这很容易减少到只有三个 colors 而没有任何差异。 此外,如果更改 colors,您几乎肯定需要更改代码。换句话说,此解决方案是“特定的”,颜色“红色”为最大值,“绿色”为最小值,“黄色”为最小值中间值。

首先,使用红色、黄色和绿色 Colors 进行观察。下面是使用的每个 colors 的组件 colors。 第一个参数是alpha值,接下来的三个参数是红色、绿色和蓝色分量值 (0-255)。 因此对于红色,红色分量设置为最大 255,绿色和蓝色值设置为最小零 (0)。 如果您查看这三个 colors 的红色、绿色和蓝色组件,您可能会注意到我们可以使用的东西……

Color Red = Color.FromArgb(255, 255, 0, 0);  // Max
Color Yellow = Color.FromArgb(255, 255, 255, 0);  // Mid
Color Green = Color.FromArgb(255, 0, 255, 0);  // Min

如果我们查看红色和黄色组件之间的“差异”,那么它们之间的唯一区别是绿色组件……黄色设置为最大 255。 同样的想法适用于黄色和绿色分量之间的差异……显然,红色分量设置为零 (0)。 colors 的这种方便的“差异”可能会有用。

如果我们知道所有数据的最大值、最小值和中间值(我们将得到),那么我们就会知道如果单元格的值小于最大值但大于中间值,那么我们就会知道颜色会属于从黄色到红色的范围,我们只需要调整绿色分量。 如果单元格值小于中,那么我们就知道单元格颜色在绿色到黄色范围内,我们只需要调整红色分量。 让我用一个简单的例子来说明。

给定上面的 colors,我们想知道所有 colors 之间有多少“不同”的可能值。绿色到黄色之间为 255,黄色和红色之间为 255。 所以只有 255x2 = 510 个不同的可能 colors。给定所有数据值,我们永远无法显示超过 510 个不同的 colors。应该注意的是,这些“不同”的 colors 在查看数字时肯定很明显,然而,这包括许多差异很小的数字,以至于用户永远不会注意到细微的“颜色”差异。

继续……

假设数据中的最高值 (Max) 为 2000,最低值 (Min) 为 0。这将使 Mid = 1000。这给了我们两个范围……即 2000-1000 是从红色到黄色的范围. 1000-0 是从黄色到绿色的范围。

接下来,我们需要粗略地计算一下我在下面的代码中所说的一个Steps (分区)。 我们将通过将所有数据的总数 (0-2000) 除以我们拥有的颜色值的数量来得到这个数字…… (510)……

2000 / 510 = 3.921

如果我们将这个数字四舍五入到 4,这意味着什么? 这告诉我们的主要事情是,给定所有数据,如果两个数字相差小于 4,那么这两个“不同”值将 map 变为“相同”颜色。 如果所有数据只有少量差异且范围很宽,这可能会成为问题。 在此处的示例中,我们将为单元格中的每 4 个值将适当的颜色分量增加 1。 例如,单元格值为 500,因此它在绿色到黄色范围内,因此我们将单元格红色分量设置为 500 / 4 = 125。这将是单元格的红色分量值。

您可以注意到,如果数据范围很大,例如 0-10,000,那么Steps数可能/会“增加”大小并且可能很大。 这意味着两个相差很大的数字可能 map 变成相同的颜色。 只是提醒一下。

下面的代码创建四 (4) 个全局decimal值( MaxMinMidSteps ),因为除非数据发生变化,否则它们不会改变。

Dim GridTable As DataTable
Dim Min As Decimal = Decimal.MaxValue
Dim Max As Decimal = Decimal.MinValue
Dim Mid As Decimal = Decimal.MinValue
Dim Steps As Decimal = Decimal.MinValue

一旦GridTable填满数据,我们将调用下面的方法循环遍历表格并设置上面的全局变量。

Private Sub SetMinMax()
  Max = Decimal.MinValue
  Min = Decimal.MaxValue
  Dim temp As Decimal
  For Each row As DataRow In GridTable.Rows
    temp = CDec(row(1))
    If temp > Max Then
      Max = temp
    End If
    If temp < Min Then
      Min = temp
    End If
  Next
  
  If ((Not (Max = Decimal.MinValue)) And (Not (Min = Decimal.MaxValue))) Then
    Dim total As Decimal = (Max - Min)
    Mid = total / 2
    Steps = total / 510
    If (Steps Mod 2 = 0) Then
      Steps = Convert.ToInt32(Steps) + 1
    End If
    Debug.WriteLine("Max: " + Max.ToString())
    Debug.WriteLine("Min: " + Min.ToString())
    Debug.WriteLine("Mid: " + Mid.ToString())
    Debug.WriteLine("Total values: " + total.ToString())
    Debug.WriteLine("Step: " + Steps.ToString())
  End If
End Sub

接下来我们的方法在给定单元格值的情况下返回正确的Color 前三个if语句查找 Max、Mid 和 Min 值,因为我们知道这些 colors 是什么。 如果代码继续,那么我们检查该数字是属于红-黄组还是黄-绿组。

如果目标数字小于中,那么我们知道我们有一个数字在绿色到黄色范围内,我们需要找到颜色的红色分量。 我们从最小值和目标值之间的差值中得到这个值。 然后我们将该数字除以 Step 值。 最后检查是否在边界内,然后我们有这个目标值的红色组件并返回它的颜色。

如果目标值小于最大值,那么我们知道我们在黄色到红色组中,需要调整颜色的绿色分量。 使用与上述相同的策略,我们通过取 Max 与目标值之间的差值来找到偏移值。 然后除以 Step 值并返回颜色。

Private Function GetColorFromValue(targetValue As Decimal) As Color
  If (targetValue = Max) Then
    Return Color.FromArgb(255, 255, 0, 0)
  End If
  If (targetValue = Mid) Then
    Return Color.FromArgb(255, 255, 255, 0)
  End If
  If (targetValue = Min) Then
    Return Color.FromArgb(255, 0, 255, 0)
  End If
  Dim offsetValue As Decimal
  Dim offsetSteps As Decimal
  Dim rgbValue As Int32
  If (targetValue < Mid) Then
    offsetValue = targetValue - Min
    offsetSteps = offsetValue / Steps
    rgbValue = Convert.ToInt32(offsetSteps)
    If (rgbValue > 255) Then
      rgbValue = 255
    End If
    Return Color.FromArgb(255, rgbValue, 255, 0)
  End If
  If (targetValue < Max) Then
    offsetValue = Max - targetValue
    offsetSteps = offsetValue / Steps
    rgbValue = Convert.ToInt32(offsetSteps)
    If (rgbValue > 255) Then
      rgbValue = 255
    End If
    Return Color.FromArgb(255, 255, rgbValue, 0)
  End If
  Return Color.White
End Function

接下来的用法是使用上述方法循环遍历网格行和 colors 单元格的方法……

Private Sub ColorCells()
  Dim dr As DataRowView
  Dim cellValue As Decimal
  For Each row As DataGridViewRow In DataGridView1.Rows
    If Not (row.IsNewRow) Then
      dr = CType(row.DataBoundItem, DataRowView)
      cellValue = CType(dr(1), Decimal)
      Dim cellColor As Color = GetColorFromValue(cellValue)
      row.Cells(1).Style.BackColor = cellColor
    End If
  Next
End Sub

为了测试,下面是一个完整的例子。 创建一个新的 VB winforms 解决方案并将DataGridView放到网格上,下面的代码应该如下所示。

在此处输入图像描述

请注意,在测试中,我做了一个广泛的范围以表明着色确实很微妙并且可以在上面看到。 您可能想要注释掉随机测试数据的循环以测试一组已知值。 最后,由于用户可以单击列 header 对网格进行排序,因此每次排序时我们都需要重新调用我们的方法。

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  GridTable = GetData()
  SetMinMax()
  DataGridView1.DataSource = GridTable
  ColorCells()
End Sub


Private Function GetData() As DataTable
  Dim rand As Random = New Random()
  Dim dt As DataTable = New DataTable()
  dt.Columns.Add("Col0", GetType(String))
  dt.Columns.Add("DataToColor", GetType(Decimal))
  dt.Columns.Add("Random", GetType(Int32))
  dt.Rows.Add("C0R0", 2000.0, rand.Next(100))
  dt.Rows.Add("C0R1", 1750.02, rand.Next(100))
  dt.Rows.Add("C0R2", 1500.45, rand.Next(100))
  dt.Rows.Add("C0R3", 1250.0, rand.Next(100))
  dt.Rows.Add("C0R4", 1000.99, rand.Next(100))
  dt.Rows.Add("C0R5", 750.23, rand.Next(100))
  dt.Rows.Add("C0R6", 500.45, rand.Next(100))
  dt.Rows.Add("C0R7", 250.55, rand.Next(100))
  dt.Rows.Add("C0R8", 5.0, rand.Next(100))
  Dim randDecimal As Decimal
  For index = 0 To 349
    randDecimal = GetRandomDecimal(rand, 9999, 99)
    dt.Rows.Add("C0R" + (index + 9).ToString(), randDecimal, rand.Next(100))
  Next
  Return dt
End Function

Public Function GetRandomDecimal(rand As Random, wholeLen As Int32, decLen As Int32) As Decimal
  Dim whole1 As Int32 = rand.Next(1, wholeLen)
  Dim dec1 As Int32 = rand.Next(0, decLen)
  Dim value As Decimal
  Decimal.TryParse(whole1.ToString() + "." + dec1.ToString(), value)
  Return value
End Function


Private Sub DataGridView1_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.ColumnHeaderMouseClick
  ColorCells()
End Sub

我希望这是有道理的。

您想要利用 DataGridView 的DataBindingComplete事件。

''' <summary>
''' Formats cell styles based on the data in certain cells.
''' </summary>
Private Sub dgvResults_DataBindingComplete(ByVal sender As Object, ByVal e As DataGridViewBindingCompleteEventArgs) Handles dgvResults.DataBindingComplete
    
    
    For Each r As DataGridViewRow In dgvResults.Rows

        ' Your code goes here to analyze the value of the cell(s).
        ' This is example code:
        If r.Cells(1).Value = "Yes" Then

            Dim style As New DataGridViewCellStyle
            style.BackColor = Color.Red
            style.ForeColor = Color.White
            style.SelectionBackColor = Color.Red
            style.SelectionForeColor = Color.White

            r.Cells(1).Style = style
            
        End If
        
    Next

End Sub

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM