簡體   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