[英]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
值( Max
、 Min
、 Mid
和Steps
),因為除非數據發生變化,否則它們不會改變。
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.