我正在尝试将图片分配到数据集的每一行的列中。数据保存在DataGridView
。
加载表单后,将填充DataGridView
,然后检查每一行以生成状态,然后将与此状态对应的图像加载到第一列。
下面的代码工作正常,但对于2000行数据集,它需要30秒以上的时间来填充图像,这太长了。我能做些什么来加快速度,或者采用另一种方法来减轻系统负载?
For i = 0 To dgvViewOrders.RowCount - 1
If dgvViewOrders.Rows(i).Cells("DepositPaid").Value = False Then
dgvViewOrders.Rows(i).Cells("ColImg").Value = My.Resources.WaitingForDeposit_25
ElseIf dgvViewOrders.Rows(i).Cells("DepositPaid").Value = True And CDate(dgvViewOrders.Rows(i).Cells("DateProcessed").Value).AddDays(20) < Now And dgvViewOrders.Rows(i).Cells("AllMaterialsDelivered").Value = False Then
dgvViewOrders.Rows(i).Cells("ColImg").Value = My.Resources.ItemsRequireOrdering_25
ElseIf dgvViewOrders.Rows(i).Cells("AllMaterialsDelivered").Value = False Then
dgvViewOrders.Rows(i).Cells("ColImg").Value = My.Resources.WaitingForDelivery_25
Else
If dgvViewOrders.Rows(i).Cells("FullPaid").Value = True Then
dgvViewOrders.Rows(i).Cells("ColImg").Value = My.Resources.Checked_25
Else
dgvViewOrders.Rows(i).Cells("ColImg").Value = My.Resources.WaitingForPayment_25
End If
End If
Next
编辑:我还应该注意,我无法轻松更改数据库标头以包含存储此状态的位置,因此必须在加载时动态完成。
更新1
使用Jinx88909
的方法我现在使用以下代码:
Private Sub dgvViewOrders_RowsAdded(sender As Object, e As DataGridViewRowsAddedEventArgs) Handles dgvViewOrders.RowsAdded
Try
Debug.Print("Processing Row: " & e.RowIndex)
If dgvViewOrders.Columns.Contains("ColImg") = False Then
Dim ColImage As DataGridViewImageColumn = New DataGridViewImageColumn
ColImage.Name = "ColImg"
dgvViewOrders.Columns.Add(ColImage)
ColImage.HeaderText = "Status"
ColImage.ImageLayout = DataGridViewImageCellLayout.Zoom
ColImage.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
End If
If dgvViewOrders.Rows(e.RowIndex).Cells("DepositPaid").Value = False Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = My.Resources.WaitingForDeposit_25
ElseIf dgvViewOrders.Rows(e.RowIndex).Cells("DepositPaid").Value = True And CDate(dgvViewOrders.Rows(e.RowIndex).Cells("DateProcessed").Value).AddDays(20) < Now And dgvViewOrders.Rows(e.RowIndex).Cells("AllMaterialsDelivered").Value = False Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = My.Resources.ItemsRequireOrdering_25
ElseIf dgvViewOrders.Rows(e.RowIndex).Cells("AllMaterialsDelivered").Value = False Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = My.Resources.WaitingForDelivery_25
Else
If dgvViewOrders.Rows(e.RowIndex).Cells("FullPaid").Value = True Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = My.Resources.Checked_25
Else
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = My.Resources.WaitingForPayment_25
End If
End If
Catch ex As Exception
Debug.Print(ex.Message)
End Try
End Sub
但似乎存在一个问题,即添加图像时会导致RowAdded
事件再次触发。我放了一个debug.print
,这是从不再调用sub之前的输出:
处理行:0, 处理行:1, 处理行:0, 处理行:1
前两行显示DataGridView
中的正确图像,但其余部分没有显示任何内容(或没有图像的红叉)。
附加说明:使用以下代码填充DataGridView
:
dgvViewOrders.DataSource = dtOrders
- 其中dtOrders
是从Access数据库获取的数据表
更新2
我尝试过将图像分配给数组的方法,并且只有当用户可以看到该行并且它才有效时才加载它们。但是当滚动DataGridView
时,响应很糟糕。所有图像都会恢复为&#34; No Image Assigned&#34;并且所有行需要大约2到3秒钟。要绘制的状态图像。
我错过了什么吗?这是我的代码:
Private Sub dgvViewOrders_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles dgvViewOrders.CellFormatting
If LoadingComplete = False Then Exit Sub
If dgvViewOrders.Rows(e.RowIndex).IsNewRow Then Return
If e.ColumnIndex <> 0 Then Return
If DirectCast(dgvViewOrders.Rows(e.RowIndex).Cells("DepositPaid").Value, Boolean) = False Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = StatusImages(2)
ElseIf DirectCast(dgvViewOrders.Rows(e.RowIndex).Cells("DepositPaid").Value, Boolean) = True And
CDate(dgvViewOrders.Rows(e.RowIndex).Cells("DateProcessed").Value).AddDays(20) < Now And
DirectCast(dgvViewOrders.Rows(e.RowIndex).Cells("AllMaterialsDelivered").Value, Boolean) = False Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = StatusImages(5)
ElseIf DirectCast(dgvViewOrders.Rows(e.RowIndex).Cells("AllMaterialsDelivered").Value, Boolean) = False Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = StatusImages(3)
Else
If DirectCast(dgvViewOrders.Rows(e.RowIndex).Cells("FullPaid").Value, Boolean) = True Then
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = StatusImages(0)
Else
dgvViewOrders.Rows(e.RowIndex).Cells("ColImg").Value = StatusImages(1)
End If
End If
End Sub
答案 0 :(得分:1)
如OP所述,当使用RowAdded
方法时,事件多次触发,仅针对第1行和第2行。经过一些研究,我发现了this,特别是这个评论:
问题:每行都会触发rowsadded事件吗?
答案:不。它被称为事件合并,每次发生数以千计的微不足道的行为之一时触发事件将是非常繁琐和处理器密集的。事实上,方法(存在BeginLoadData / EndLoadData以完全关闭事件触发,因为它只会减慢一切。
相反,我查看了RowStateChanged方法:
Private Sub DataGridView1_RowStateChanged(sender As Object, e As DataGridViewRowStateChangedEventArgs) Handles DataGridView1.RowStateChanged
If DataGridView1.Columns.Contains("ColImg") = False Then
Dim ColImage As DataGridViewImageColumn = New DataGridViewImageColumn
ColImage.Name = "ColImg"
DataGridView1.Columns.Add(ColImage)
ColImage.HeaderText = "Status"
ColImage.ImageLayout = DataGridViewImageCellLayout.Zoom
ColImage.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
End If
If e.StateChanged = DataGridViewElementStates.Displayed Then
Debug.Print("Processing row: " & e.Row.Index.ToString)
DataGridView1.Rows(e.Row.Index).Cells("ColImg").Value = My.Resources.Image1
End If
End Sub
这就是我加载数据的方式:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
_dt = New DataTable
DataGridView1.DataSource = _dt
Using con As New SqlConnection(connection),
com As New SqlCommand(command, con)
con.Open()
_dt.Load(com.ExecuteReader)
End Using
End Sub
请注意,我在填写之前将
_dt
分配给DataGridView1.DataSource
。通过执行此操作,事件将按预期触发。
这是我的图片的截图(我很欣赏它的垃圾,但不得不快速嘲笑):
这就是你的代码的样子:
Private Sub dgvViewOrders_RowStateChanged(sender As Object, e As DataGridViewRowStateChangedEventArgs) Handles dgvViewOrders.RowStateChanged
If DataGridView1.Columns.Contains("ColImg") = False Then
Dim ColImage As DataGridViewImageColumn = New DataGridViewImageColumn
ColImage.Name = "ColImg"
DataGridView1.Columns.Add(ColImage)
ColImage.HeaderText = "Status"
ColImage.ImageLayout = DataGridViewImageCellLayout.Zoom
ColImage.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
End If
If e.StateChanged = DataGridViewElementStates.Displayed Then
If dgvViewOrders.Rows(e.Row.Index).Cells("DepositPaid").Value = False Then
dgvViewOrders.Rows(e.Row.Index).Cells("ColImg").Value = My.Resources.WaitingForDeposit_25
ElseIf dgvViewOrders.Rows(e.Row.Index).Cells("DepositPaid").Value = True And CDate(dgvViewOrders.Rows(e.Row.Index).Cells("DateProcessed").Value).AddDays(20) < Now And dgvViewOrders.Rows(e.Row.Index).Cells("AllMaterialsDelivered").Value = False Then
dgvViewOrders.Rows(e.Row.Index).Cells("ColImg").Value = My.Resources.ItemsRequireOrdering_25
ElseIf dgvViewOrders.Rows(e.Row.Index).Cells("AllMaterialsDelivered").Value = False Then
dgvViewOrders.Rows(e.Row.Index).Cells("ColImg").Value = My.Resources.WaitingForDelivery_25
Else
If dgvViewOrders.Rows(e.Row.Index).Cells("FullPaid").Value = True Then
dgvViewOrders.Rows(e.Row.Index).Cells("ColImg").Value = My.Resources.Checked_25
Else
dgvViewOrders.Rows(e.Row.Index).Cells("ColImg").Value = My.Resources.WaitingForPayment_25
End If
End If
End If
End Sub
答案 1 :(得分:1)
发布的代码中有几个初步的内容:
启用Option Strict 此代码无法编译:
If dgvViewOrders.Rows(i).Cells("DepositPaid").Value = False Then
DataGridView
小区返回/暂停Object
,您无法将Object
与Boolean
进行比较
从概念上讲,驳回了The data is held in a DataGridView
的概念,因此你必须&#34;必须&#34;循环遍历所有行。由于您使用的是DataSource
,因此数据位于此处,控件只是向用户显示数据视图的方法。
在大多数情况下,对数据进行操作比在UI控件上进行操作要快很多倍。 This Q&A example,做一些东西需要17秒左右。在这种情况下,使用数据会将时间缩短到毫秒。这里做的事情要少得多,一切都可以完成。
此外,您的应用正在泄露:
dgvViewOrders.Rows(i).Cells("ColImg").Value = My.Resources.WaitingForDelivery_25
这是每次创建一个新的图像对象。你的2000行不需要2000个单独的对象;等待传递的所有行都可以显示相同的图像。此外,创建该对象后,您的代码需要处理任何以前的图像(例如,当状态发生变化时)。
为避免泄漏,请将图像加载到数组中:
' form level declatation
Private ImgBalls As Image()
...
' elsewhere, load the images from resources:
ImgBalls = New Image() {
My.Resources.ballblack, My.Resources.ballblue,
My.Resources.ballgreen,
My.Resources.ballorange, My.Resources.ballpurple,
My.Resources.ballred, My.Resources.ballyellow
}
为了分配图像,您可以迭代DataSource中的所有行并设置指示状态的代码,但在这种情况下更好的是使用其中一个DGV事件。有两个好的候选人:RowPrePaint
和CellFormatting
。两者都是高效的:如果用户从不滚动到第1565行,则对其执行操作的代码永远不会运行。
此代码还使用Enum
索引图像(Status.Blue
)而不是幻数:
Private Sub dgv1_CellFormatting(sender As Object,..etc etc
' exit if the new user row, or not the ImgCol
If dgv1.Rows(e.RowIndex).IsNewRow Then Return
If e.ColumnIndex <> 8 Then Return
If DirectCast(dgv1.Rows(e.RowIndex).Cells("Value").Value, Int32) <= 3 Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Green)
ElseIf DirectCast(dgv1.Rows(e.RowIndex).Cells("Animal").Value, String) = "rat" Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Black)
ElseIf DirectCast(dgv1.Rows(e.RowIndex).Cells("Price").Value, Decimal) > 14.99 Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Purple)
Else
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Yellow)
End If
End Sub
将事件用于此类事情的好处是代码仅作用于用户查看的行。当处理时间被分解为比特时,更难以注意到。
结果:
因为代码为使用蓝色图像/状态的所有人分配相同的图像对象,所以它不再泄漏资源。
使用各种滚动方法的性能:
我添加了另一个布尔列,以更接近地模拟您在代码中显示的测试/条件。它还使用默认的问号图像代替标准&#34;破坏&#34;图片。动画显示a)使用向下拇指,b)鼠标滚轮然后c)拖动拇指。最后一个允许多达10行绘制有一点延迟,但是在2-3秒附近没有(即使在帖子中有动画GIF的滞后呈现 - 下载到本地运行并且它更快)。 / p>
也就是说,您可以采取一些措施来优化代码:
AndAlso
而非And
(并使用OrElse
而不是Or
)进行布尔测试。一旦失败,这些将短路并且不评估其他条件。DepositPaid
和AllMaterialsDelivered
,这样如果其中任何一个为假,则不需要具有日期的hokey-pokey(我不希望这会产生重大差异,但转换bool似乎应该比日期的东西更简单,更快。)总的来说,你可能还有其他东西吃点时间。动画图像的代码与此类似:
' exit if the new user row, or not the ImgCol
If dgv1.Rows(e.RowIndex).IsNewRow Then Return
If e.ColumnIndex <> 9 Then Return
If DirectCast(dgv1.Rows(e.RowIndex).Cells("Active").Value, Boolean) = False Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Green)
ElseIf DirectCast(dgv1.Rows(e.RowIndex).Cells("Active").Value, Boolean) = True AndAlso
DirectCast(dgv1.Rows(e.RowIndex).Cells("Delivered").Value, Boolean) = False AndAlso
DirectCast(dgv1.Rows(e.RowIndex).Cells("ItemDate").Value, DateTime).AddDays(20) > DateTime.Now Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Purple)
ElseIf DirectCast(dgv1.Rows(e.RowIndex).Cells("Price").Value, Decimal) > 14.99 Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Orange)
Else
If DirectCast(dgv1.Rows(e.RowIndex).Cells("Value").Value, Int32) < 5 Then
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Yellow)
Else
dgv1.Rows(e.RowIndex).Cells("ImgCol").Value = ImgBalls(Status.Blue)
End If
End If