My DataGrid has specific columns that I want to apply a background color to the cell if the value is the 1st, 2nd, or 3rd best value for that column. The datatypes are all primitives, either string, int, byte, or double.
In my old VB project that used Winforms, I would do the following: - Loop through the columns and choose only those that I wanted to colorize; - For each column I would extract all distinct values from all rows of that cell; - I would sort the values and determine the top three values, either ascending or descending, based on my preference for that individual row; - I would loop through the columns again and color the cell background color if it matched one of the top three values. I used Yellow for first, LightGreen for second, and LightBlue for first.
I have found great examples of setting up a value converter but I do not know a good way to look at all of the data for that particular column. And WPF's DataGrid doesn't seem to allow you to access individual cells, as far as I can tell.
Am I wrong? Is there a way to do this?
Here's the 8-year old VB code for a WinForms grid for reference - it does a bit more than just colorize the cells. I can't find a good way to even get started in WPF to do this.
Private Sub ColorizeRows(ByRef dgv As DataGridView) ' Colorizes the rows Dim colors() As System.Drawing.Color = {Color.Yellow, Color.Orange, Color.LightBlue}
For Each column As DataGridViewColumn In dgv.Columns
If column.Visible = True Then
Dim vals() As Object = GetDistinctValuesFromColumn(dgv, column.Index)
Select Case column.ToolTipText.ToLower()
Case "d"
For Each row As DataGridViewRow In dgv.Rows
row.Cells(column.Index).Style.BackColor = dgv.Columns(column.Index).DefaultCellStyle.BackColor
Next
Dim lowCount As Integer = (vals.GetUpperBound(0) + 1) - 3
If lowCount < 0 Then lowCount = 0
For j As Integer = vals.GetUpperBound(0) To lowCount Step -1
For Each row As DataGridViewRow In dgv.Rows
If (Not (row.Cells(column.Index).FormattedValue Is DBNull.Value)) Then
If CStr(row.Cells(column.Index).FormattedValue) = CStr(String.Format("{0:n1}", vals(j))) Then
row.Cells(column.Index).Style.BackColor = colors(vals.GetUpperBound(0) - j)
End If
End If
Next
Next
Case "a"
' First de-colorize the row
For Each row As DataGridViewRow In dgv.Rows
row.Cells(column.Index).Style.BackColor = dgv.Columns(column.Index).DefaultCellStyle.BackColor
Next
Dim lowCount As Integer = 2
If lowCount > vals.GetUpperBound(0) Then lowCount = vals.GetUpperBound(0)
For j As Integer = 0 To lowCount
For Each row As DataGridViewRow In dgv.Rows
If (Not (row.Cells(column.Index).FormattedValue Is DBNull.Value)) Then
If CStr(row.Cells(column.Index).FormattedValue) = CStr(String.Format("{0:n1}", vals(j))) Then
row.Cells(column.Index).Style.BackColor = colors(j)
End If
End If
Next
Next
Case Else
' do nothing
End Select
' Copy the highlighting rules from the EGB box to the EGL box if we're on the dgv
If dgv.Name = "dgvRace" Then
For i As Integer = 0 To dgv.Rows.Count - 1
dgv.Rows(i).Cells("effGradeLetter").Style = dgv.Rows(i).Cells("effGradeLarge").Style
Next
End If
' Update all the tooltips for each box
Dim places() As String = {"1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th"}
For Each row As DataGridViewRow In dgv.Rows
' Update tooltiptext for each row
Dim place As Integer = FindIndexInArray(vals, row.Cells(column.Index).FormattedValue)
If place > -1 Then
Dim placeStr As String = IIf(column.ToolTipText.ToLower() = "d", places(vals.GetUpperBound(0) - place), places(place))
'If column.ToolTipText.ToLower() = "d" Then place = vals.GetUpperBound(0) - place
row.Cells(column.Index).ToolTipText = String.Format("Column Place {0}", placeStr)
If row.Cells(column.Index).Value.GetType().ToString() = "System.Single" Or row.Cells(column.Index).Value.GetType().ToString() = "System.Int32" Then
' Get upper/lower difference if available
If place > 0 Then
Dim distFromPlaceBefore As Single = Math.Abs(CSng(vals(place)) - CSng(vals(place - 1)))
Dim distFromFirstPlace As Single = Math.Abs(CSng(vals(place)) - CSng(vals(0)))
row.Cells(column.Index).ToolTipText &= ControlChars.CrLf & String.Format(" - Distance from {0}: {1:n1}; Distance from 1st: {2:n1}", places(place - 1), distFromPlaceBefore, distFromFirstPlace)
End If
If place < vals.GetUpperBound(0) Then
Dim distFromPlaceAfter As Single = Math.Abs(Math.Round(CSng(vals(place)) - CSng(vals(place + 1)), 1))
Dim distFromLastPlace As Single = Math.Abs(Math.Round(CSng(vals(place)) - CSng(vals(vals.GetUpperBound(0))), 1))
row.Cells(column.Index).ToolTipText &= ControlChars.CrLf & String.Format(" - Distance from {0}: {1:n1}; Distance from {3}: {2:n1}", places(place + 1), distFromPlaceAfter, distFromLastPlace, places(vals.GetUpperBound(0)))
End If
End If
End If
Next
End If
Next
UPDATE: Could I somehow attach the column and the source grid so I could get the values and get what I need to make the coloring decision?
Take a look at this article. I think that is just what you need. Pay attention to the ElementStyle with MultiBinding and CellColorConverter.