英文:
How to add different colour icons/images automatic to the Row Header of a DataGridView based on value in column1
问题
如何根据列1中的值自动将不同颜色的图标/图像添加到DataGridView的行标题中?
是否可以在不手动设置基于“列1”中的值的颜色的情况下随机选择所有颜色,还可以考虑“列1”和“列2”之间的值组合?
谢谢
Private colors As Color()
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
colors = {Color.Red, Color.Green, Color.Orange, Color.Black}
Dim Table1 = New DataTable("TableName")
Table1.Columns.AddRange({
New DataColumn("Column1", GetType(String)),
New DataColumn("Column2", GetType(Integer)),
New DataColumn("Column3", GetType(Integer))
})
Table1.Rows.Add("Item1", 44, 99)
Table1.Rows.Add("Item2", 50, 70)
Table1.Rows.Add("Item3", 75, 85)
Table1.Rows.Add("Item2", 60, 70)
Table1.Rows.Add("Item3", 85, 85)
Table1.Rows.Add("Item4", 77, 21)
Table1.Rows.Add("Item2", 60, 70)
DataGridView1.RowTemplate.Height = 48
DataGridView1.RowHeadersWidth = 48
DataGridView1.DataSource = Table1
End Sub
Private Sub DataGridView1_CellPainting(
sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso
e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim g = e.Graphics
Dim sz = Math.Min(e.CellBounds.Width, e.CellBounds.Height) - 6
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - sz) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - sz) \ 2,
sz, sz)
Dim imgRect = Rectangle.Inflate(ellipseRect, -3, -3)
Dim colorIndex = e.RowIndex Mod colors.Length
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
DataGridViewPaintParts.Border Or
DataGridViewPaintParts.SelectionBackground)
Using bmp = My.Resources.SomeImage,
ellipseBrush = New SolidBrush(colors(colorIndex))
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillEllipse(ellipseBrush, ellipseRect)
g.SmoothingMode = SmoothingMode.None
g.DrawImage(bmp, imgRect,
0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel)
End Using
e.Handled = True
End If
End Sub
英文:
How to add different colour icons/images automatic to the Row Header of a DataGridView based on value in column1?
is it possible to pick up all the colors randomly without manually setting the color based on the value in "COLUMN1" and can also take the value in combination between "COLUMN1" and "COLUMN2"?
Thanks
Private colors As Color()
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
colors = {Color.Red, Color.Green, Color.Orange, Color.Black}
Dim Table1 = New DataTable("TableName")
Table1.Columns.AddRange({
New DataColumn("Column1", GetType(String)),
New DataColumn("Column2", GetType(Integer)),
New DataColumn("Column3", GetType(Integer))
})
Table1.Rows.Add("Item1", 44, 99)
Table1.Rows.Add("Item2", 50, 70)
Table1.Rows.Add("Item3", 75, 85)
Table1.Rows.Add("Item2", 60, 70)
Table1.Rows.Add("Item3", 85, 85)
Table1.Rows.Add("Item4", 77, 21)
Table1.Rows.Add("Item2", 60, 70)
DataGridView1.RowTemplate.Height = 48
DataGridView1.RowHeadersWidth = 48
DataGridView1.DataSource = Table1
End Sub
Private Sub DataGridView1_CellPainting(
sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso
e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim g = e.Graphics
Dim sz = Math.Min(e.CellBounds.Width, e.CellBounds.Height) - 6
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - sz) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - sz) \ 2,
sz, sz)
Dim imgRect = Rectangle.Inflate(ellipseRect, -3, -3)
Dim colorIndex = e.RowIndex Mod colors.Length
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
DataGridViewPaintParts.Border Or
DataGridViewPaintParts.SelectionBackground)
Using bmp = My.Resources.SomeImage,
ellipseBrush = New SolidBrush(colors(colorIndex))
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillEllipse(ellipseBrush, ellipseRect)
g.SmoothingMode = SmoothingMode.None
g.DrawImage(bmp, imgRect,
0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel)
End Using
e.Handled = True
End If
End Sub
答案1
得分: 1
使用相同的随机颜色来标识重复行吗?为了实现这个目标,你需要根据单元格的值对行进行分组,并为每个分组使用一种颜色。在CellPainting
事件中执行这个操作是一项繁重的任务,因为它对网格中的每个单元格都会触发。因此,我建议在DataTable
中添加一个隐藏的DataColumn
,用于保存每行的颜色索引。这些索引在首次绑定控件时设置,以及用户修改值时更新。
' +
Imports System.Reflection
Private colors As Color()
Private bmp As Bitmap
Sub New()
InitializeComponent()
' 减少闪烁...
DataGridView1.GetType().
GetProperty("DoubleBuffered",
BindingFlags.Instance Or BindingFlags.NonPublic).
SetValue(DataGridView1, True)
End Sub
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
' 收集深色...
colors = GetType(Color).
GetProperties(BindingFlags.Public Or BindingFlags.Static).
Where(Function(pi) pi.PropertyType = GetType(Color)).
Select(Function(pi) CType(pi.GetValue(GetType(Color), Nothing), Color)).
Where(Function(c)
Return ((
c.R * 0.299F +
c.G * 0.587F +
c.B * 0.114F) / 256.0F) <= 0.5F
End Function).ToArray()
bmp = My.Resources.Money
Dim Table1 = New DataTable("TableName")
Table1.Columns.AddRange({
New DataColumn("Column1", GetType(String)),
New DataColumn("Column2", GetType(Integer)),
New DataColumn("Column3", GetType(Integer)),
New DataColumn("ColorIndex", GetType(Integer))
})
Table1.Rows.Add("Item1", 44, 99)
Table1.Rows.Add("Item2", 50, 70)
Table1.Rows.Add("Item3", 75, 85)
Table1.Rows.Add("Item2", 60, 70)
Table1.Rows.Add("Item3", 75, 85)
Table1.Rows.Add("Item4", 77, 21)
Table1.Rows.Add("Item2", 50, 70)
' ...等等。
DataGridView1.DataSource = Table1
DataGridView1.Columns("ColorIndex").Visible = False
UpdateColorColumn()
Table1.AcceptChanges()
End Sub
Protected Overrides Sub OnFormClosed(e As FormClosedEventArgs)
MyBase.OnFormClosed(e)
bmp.Dispose()
DirectCast(DataGridView1.DataSource, IDisposable)?.Dispose()
End Sub
Private Sub DataGridView1_CellValueChanged(sender As Object,
e As DataGridViewCellEventArgs) _
Handles DataGridView1.CellValueChanged
UpdateColorColumn()
End Sub
Private Sub DataGridView1_CellPainting(sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim ellipseSize = DataGridView1.RowTemplate.Height - 3
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - ellipseSize) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - ellipseSize) \ 2,
ellipseSize, ellipseSize)
Dim imgSize = ellipseSize ' 或者更小...
Dim imgRect = New Rectangle(
ellipseRect.X + (ellipseRect.Width - imgSize) \ 2,
ellipseRect.Y + (ellipseRect.Height - imgSize) \ 2,
imgSize, imgSize)
Dim drv = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView)
Dim colorIndex = Convert.ToInt32(drv.Item("ColorIndex"))
Dim g = e.Graphics
Dim gs = g.Save()
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
DataGridViewPaintParts.Border Or
DataGridViewPaintParts.SelectionBackground)
Using ellipseBrush = New SolidBrush(colors(colorIndex))
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillEllipse(ellipseBrush, ellipseRect)
g.InterpolationMode = InterpolationMode.HighQualityBicubic
g.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel)
End Using
g.Restore(gs)
e.Handled = True
End If
End Sub
Private Sub UpdateColorColumn()
Dim dt = DirectCast(DataGridView1.DataSource, DataTable)
dt.EndInit()
Dim groups = dt.AsEnumerable().
GroupBy(Function(g) New With {
Key .Col1 = g.Item(0),
Key .Col2 = g.Item(1),
Key .Col3 = g.Item(2) ' 移除此处以便根据前两个分组。
})
For i = 0 To groups.Count - 1
Dim group = groups(i)
Dim colorIndex = i Mod colors.Length
For Each row In group
row("ColorIndex") = colorIndex
Next
Next
End Sub
如果你有一个数据库,请在添加ColorIndex
列之前填充DataTable
。要更新数据库,你不需要特别处理额外的列。INSERT
和UPDATE
命令会执行它们的SQL CommandText
查询,并忽略Columns
集合中的其他内容。不过,你可以获取当前DataTable
的副本,并排除非数据库列。
例如:
Dim dt = Table1.DefaultView.
ToTable(False, Table1.Columns.
Cast(Of DataColumn).
Where(Function(c) c.ColumnName <> "ColorIndex").
Select(Function(c) c.ColumnName).ToArray())
作为另一种选择,你可以使用DataGridViewRow.Tag
属性来保存颜色索引。下面是另一种写法:
Private colors As Color()
' ...
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
' ...
DataGridView1.DataSource = Table1
UpdateColorIndices()
End Sub
' ...
Private Sub DataGridView1_CellPainting(sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim ellipseSize = DataGridView1.RowTemplate.Height - 3
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - ellipseSize) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - ellipseSize) \ 2,
ellipseSize, ellipseSize)
Dim imgSize = ellipseSize
Dim imgRect = New Rectangle(
ellipseRect.X + (ellipseRect.Width - imgSize) \ 2,
ellipseRect.Y
<details>
<summary>英文:</summary>
So, you mean using the same random color for the duplicate rows? For that, you need to group the rows by the cell values and use a color for each group. Doing this in the `CellPainting` event is a heavy task since it's being raised for each cell in the grid. Hence, I suggest adding a hidden `DataColumn` to the `DataTable` to keep the color index of each row. The indices are set when you bind the control the first time, and when the user modifies the values.
```vb.net
' +
Imports System.Reflection
Private colors As Color()
Private bmp As Bitmap
Sub New()
InitializeComponent()
' To reduce the flickering...
DataGridView1.GetType().
GetProperty("DoubleBuffered",
BindingFlags.Instance Or BindingFlags.NonPublic).
SetValue(DataGridView1, True)
End Sub
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
' Collect dark colors...
colors = GetType(Color).
GetProperties(BindingFlags.Public Or BindingFlags.Static).
Where(Function(pi) pi.PropertyType = GetType(Color)).
Select(Function(pi) CType(pi.GetValue(GetType(Color), Nothing), Color)).
Where(Function(c)
Return ((
c.R * 0.299F +
c.G * 0.587F +
c.B * 0.114F) / 256.0F) <= 0.5F
End Function).ToArray()
bmp = My.Resources.Money
Dim Table1 = New DataTable("TableName")
Table1.Columns.AddRange({
New DataColumn("Column1", GetType(String)),
New DataColumn("Column2", GetType(Integer)),
New DataColumn("Column3", GetType(Integer)),
New DataColumn("ColorIndex", GetType(Integer))
})
Table1.Rows.Add("Item1", 44, 99)
Table1.Rows.Add("Item2", 50, 70)
Table1.Rows.Add("Item3", 75, 85)
Table1.Rows.Add("Item2", 60, 70)
Table1.Rows.Add("Item3", 75, 85)
Table1.Rows.Add("Item4", 77, 21)
Table1.Rows.Add("Item2", 50, 70)
' ...etc.
DataGridView1.DataSource = Table1
DataGridView1.Columns("ColorIndex").Visible = False
UpdateColorColumn()
Table1.AcceptChanges()
End Sub
Protected Overrides Sub OnFormClosed(e As FormClosedEventArgs)
MyBase.OnFormClosed(e)
bmp.Dispose()
DirectCast(DataGridView1.DataSource, IDisposable)?.Dispose()
End Sub
Private Sub DataGridView1_CellValueChanged(sender As Object,
e As DataGridViewCellEventArgs) _
Handles DataGridView1.CellValueChanged
UpdateColorColumn()
End Sub
Private Sub DataGridView1_CellPainting(sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim ellipseSize = DataGridView1.RowTemplate.Height - 3
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - ellipseSize) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - ellipseSize) \ 2,
ellipseSize, ellipseSize)
Dim imgSize = ellipseSize ' Or smaller...
Dim imgRect = New Rectangle(
ellipseRect.X + (ellipseRect.Width - imgSize) \ 2,
ellipseRect.Y + (ellipseRect.Height - imgSize) \ 2,
imgSize, imgSize)
Dim drv = DirectCast(DataGridView1.Rows(e.RowIndex).DataBoundItem, DataRowView)
Dim colorIndex = Convert.ToInt32(drv.Item("ColorIndex"))
Dim g = e.Graphics
Dim gs = g.Save()
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
DataGridViewPaintParts.Border Or
DataGridViewPaintParts.SelectionBackground)
Using ellipseBrush = New SolidBrush(colors(colorIndex))
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillEllipse(ellipseBrush, ellipseRect)
g.InterpolationMode = InterpolationMode.HighQualityBicubic
g.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel)
End Using
g.Restore(gs)
e.Handled = True
End If
End Sub
Private Sub UpdateColorColumn()
Dim dt = DirectCast(DataGridView1.DataSource, DataTable)
dt.EndInit()
Dim groups = dt.AsEnumerable().
GroupBy(Function(g) New With {
Key .Col1 = g.Item(0),
Key .Col2 = g.Item(1),
Key .Col3 = g.Item(2) ' Remove this to group by the first two.
})
For i = 0 To groups.Count - 1
Dim group = groups(i)
Dim colorIndex = i Mod colors.Length
For Each row In group
row("ColorIndex") = colorIndex
Next
Next
End Sub
If you have a DataBase, fill the DataTable
before you add the ColorIndex
column. To update the DB, you don't need to do anything special regarding the additional column(s). The INSERT
and UPDATE
commands execute their SQL CommandText
queries and ignore anything else in the Columns
collection. However, you can get a copy of the current DataTable
and exclude the non-database columns.
For example:
Dim dt = Table1.DefaultView.
ToTable(False, Table1.Columns.
Cast(Of DataColumn).
Where(Function(c) c.ColumnName <> "ColorIndex").
Select(Function(c) c.ColumnName).ToArray())
As another option, use the DataGridViewRow.Tag
property to keep the color index. The same could have been written like this.
Private colors As Color()
' ...
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
' ...
DataGridView1.DataSource = Table1
UpdateColorIndices()
End Sub
' ...
Private Sub DataGridView1_CellPainting(sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim ellipseSize = DataGridView1.RowTemplate.Height - 3
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - ellipseSize) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - ellipseSize) \ 2,
ellipseSize, ellipseSize)
Dim imgSize = ellipseSize
Dim imgRect = New Rectangle(
ellipseRect.X + (ellipseRect.Width - imgSize) \ 2,
ellipseRect.Y + (ellipseRect.Height - imgSize) \ 2,
imgSize, imgSize)
Dim colorIndex = Convert.ToInt32(DataGridView1.Rows(e.RowIndex).Tag)
Dim g = e.Graphics
Dim gs = g.Save()
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
DataGridViewPaintParts.Border Or
DataGridViewPaintParts.SelectionBackground)
Using ellipseBrush = New SolidBrush(colors(colorIndex))
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillEllipse(ellipseBrush, ellipseRect)
g.InterpolationMode = InterpolationMode.HighQualityBicubic
g.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel)
End Using
g.Restore(gs)
e.Handled = True
End If
End Sub
Private Sub UpdateColorIndices()
DataGridView1.EndEdit()
Dim groups = DataGridView1.Rows.Cast(Of DataGridViewRow).
Where(Function(r) r.Index <> DataGridView1.NewRowIndex).
GroupBy(Function(r) New With {
Key .Col0 = r.Cells(0).Value,
Key .Col1 = r.Cells(1).Value,
Key .Col2 = r.Cells(2).Value
})
For i = 0 To groups.Count - 1
Dim index = i Mod colors.Length
For Each row In groups(i)
row.Tag = index
Next
Next
DataGridView1.Invalidate()
End Sub
Also, you could create a Dictionay(Of Integer, Color)
where each KeyValuePair
holds the row index and the ellipse color.
Private colors As Color()
Private ReadOnly dictColors As New Dictionary(Of Integer, Color)
' ...
Private Sub DataGridView1_CellPainting(sender As Object,
e As DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
If e.RowIndex >= 0 AndAlso e.ColumnIndex = -1 AndAlso
e.RowIndex <> DataGridView1.NewRowIndex Then
Dim ellipseSize = DataGridView1.RowTemplate.Height - 3
Dim ellipseRect = New Rectangle(
e.CellBounds.X + (e.CellBounds.Width - ellipseSize) \ 2,
e.CellBounds.Y + (e.CellBounds.Height - ellipseSize) \ 2,
ellipseSize, ellipseSize)
Dim imgSize = ellipseSize
Dim imgRect = New Rectangle(
ellipseRect.X + (ellipseRect.Width - imgSize) \ 2,
ellipseRect.Y + (ellipseRect.Height - imgSize) \ 2,
imgSize, imgSize)
Dim g = e.Graphics
Dim gs = g.Save()
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or
DataGridViewPaintParts.Border Or
DataGridViewPaintParts.SelectionBackground)
Using ellipseBrush = New SolidBrush(dictColors(e.RowIndex))
g.SmoothingMode = SmoothingMode.AntiAlias
g.FillEllipse(ellipseBrush, ellipseRect)
g.InterpolationMode = InterpolationMode.HighQualityBicubic
g.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel)
End Using
g.Restore(gs)
e.Handled = True
End If
End Sub
Private Sub UpdateColorIndices()
DataGridView1.EndEdit()
dictColors.Clear()
Dim groups = DataGridView1.Rows.Cast(Of DataGridViewRow).
Where(Function(r) r.Index <> DataGridView1.NewRowIndex).
GroupBy(Function(r) New With {
Key .Col0 = r.Cells(0).Value,
Key .Col1 = r.Cells(1).Value,
Key .Col2 = r.Cells(2).Value
})
For i = 0 To groups.Count - 1
Dim index = i Mod colors.Length
For Each row In groups(i)
dictColors(row.Index) = colors(index)
Next
Next
DataGridView1.Invalidate()
End Sub
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论