在 DataGridView 中动画图像 [英] Animate Images in DataGridView
问题描述
为了在 DataGridView
中对图像进行动画处理,我编写了下面的帮助程序类,但该类不起作用(图像未设置动画).
I wrote the helper class below in order to animate images in a DataGridView
, which is not working (images aren't animated).
在此之前,我在文中找到了一些示例代码,但它们也不起作用.
Before that, I found some sample code on the wen, but they didn't work either.
我想了解它是如何工作的,而不是仅仅因为它有效而简单地将一段代码塞进我的应用程序中.为什么我的代码没有达到预期的效果?
I want to understand how this works instead of simply shove a piece of code inside my app just because it works. Why does my code not do what it is expected to?
我发现了它不工作的原因.源 DataTable
本身不包含图像:它们通过其 CellFormatting
处理程序方法分配给代码中其他位置的 DataGridView
单元格.由于此事件也一直触发,因此总是传递一个新的图像对象,因此它始终显示图像的第 1 帧.当我创建一个新列,其中存储了本机图像值时,它们会根据需要进行动画处理.
I discovered the reason why it isn't working. The source DataTable
itself don't contain images: they are assigned to DataGridView
's Cells elsewhere in the code by its CellFormatting
handler method. Since this event also triggers all the time, a fresh image object is always passed, so it keeps always showing the image's frame #1. When I created a new column with native image values stored in it, they animated as desired.
现在的问题是:是否可以在 DataGridView
的 中对分配给
事件处理方法?.FormattedValue
属性的图像进行动画处理CellFormatting
The question now is: is it possible to animate images that are assigned to the .FormattedValue
property inside DataGridView
's CellFormatting
event handler method?
Public Class DataGridViewImageAnimator
Private WithEvents MyDataGridView As DataGridView
Public Sub New(dataGridView As DataGridView)
MyDataGridView = dataGridView
End Sub
Private MyAnimatedImages As New Dictionary(Of Point, Image)
Private Sub ImageAnimator_FrameChanged(sender As Object, e As EventArgs)
Dim imageCells = MyDataGridView.Rows.Cast(Of DataGridViewRow).SelectMany(
Function(dgvr) dgvr.Cells.OfType(Of DataGridViewImageCell))
For Each cell In imageCells
Dim img = TryCast(cell.FormattedValue, Image)
If img IsNot Nothing AndAlso MyAnimatedImages.ContainsValue(img) Then
MyDataGridView.InvalidateCell(cell)
End If
Next
End Sub
Private Sub MyDataGridView_CellPainting(
sender As Object,
e As DataGridViewCellPaintingEventArgs
) Handles MyDataGridView.CellPainting
If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
Dim cell = MyDataGridView(e.ColumnIndex, e.RowIndex)
Dim drawPoint = MyDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, True).Location
Dim pt = New Point(e.ColumnIndex, e.RowIndex)
Dim cellImg = TryCast(cell.FormattedValue, Image)
If MyAnimatedImages.ContainsKey(pt) AndAlso Equals(MyAnimatedImages(pt), cellImg) Then
'If image is already registered as animated, and is still in cell
ImageAnimator.UpdateFrames()
e.Graphics.DrawImage(cellImg, drawPoint)
Else
If MyAnimatedImages.ContainsKey(pt) Then
'If image registered as animated is no longer in cell
ImageAnimator.StopAnimate(MyAnimatedImages(pt), AddressOf ImageAnimator_FrameChanged)
MyAnimatedImages.Remove(pt)
End If
If cellImg IsNot Nothing AndAlso ImageAnimator.CanAnimate(cellImg) Then
'If cell contains an image not yet registered as animated
MyAnimatedImages(pt) = cellImg
ImageAnimator.Animate(MyAnimatedImages(pt), AddressOf ImageAnimator_FrameChanged)
ImageAnimator.UpdateFrames()
e.Graphics.DrawImage(cellImg, drawPoint)
End If
End If
End If
End Sub
End Class
推荐答案
带有自定义单元格的自定义列提供了一些优势.
A custom Column with custom Cells offers some advantages.
所有设计逻辑都集中在一个地方,可以在设计时使用 DataGridView
设计器将其选为列模板.
All the design logic is confined in one place and it can be selected as a Column template at design time using the DataGridView
designer.
性能非常好(用 200 个动画单元测试),我没有注意到任何闪烁.
The performace is quite good (tested with 200 animated cells) and I didn't notice any flickering.
动画 Gif 可以像往常一样拉伸或缩放,使用设计器设置,通过代码或手动调整行/列的大小.
The animated Gifs can be Stretched or Zoomed as usual, using the designer settings, by code or manually resizing the Rows/Columns.
但是,我不能认为它是完整的,因为我找不到使用此自定义 Column 类方法或属性启动所有动画的好方法.
However, I can't consider it complete, because I couldn't find out a good way to start all the animations using this custom Column class methods or properties.
向 DataGridView
添加了一个扩展方法(DataGridView.Animate()
).
这允许隐藏无效过程.DataGridView
数据绑定完成后,调用扩展方法即可:
Added an Extension method to the DataGridView
(DataGridView.Animate()
).
This allows to hide the invalidating procedure.
After the DataGridView
Data Binding is complete, simply call the extension method:
DataGridView1.DataSource = [DataSource]
DataGridView1.Animate()
包含扩展方法的模块:
The Module containing the Extension method:
Imports System.Runtime.CompilerServices
Module DGVExtesions
<Extension()>
Public Sub Animate(ByVal AnimatedGrid As DataGridView)
Try
For Each row As DataGridViewRow In AnimatedGrid.Rows
For Each cell As DataGridViewCell In row.Cells.OfType(Of AnimatedDGVColumn.AnimatedCell)()
AnimatedGrid.InvalidateCell(cell)
Next
Next
Catch ex As Exception
Trace.WriteLine("Exception: {0}", ex.Message)
End Try
End Sub
End Module
当然这还不够好.需要进行更多研究.
Of course this is still not good enough. Some more research is needed.
这是自定义动画列类:
This is the custom Animated Column class:
Imports System.ComponentModel
Imports System.Windows.Forms
Public Class AnimatedDGVColumn
Inherits System.Windows.Forms.DataGridViewColumn
Private custCellTemplate As AnimatedCell
Public Sub New()
Me.custCellTemplate = New AnimatedCell
Me.custCellTemplate.ImageLayout = DataGridViewImageCellLayout.Zoom
MyBase.CellTemplate = custCellTemplate
Me.AutoSizeMode = DataGridViewAutoSizeColumnMode.None
Me.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
End Sub
<Description("The ImageLayout in the Cells for this Column"), Category("Appearance")> _
<EditorBrowsable(EditorBrowsableState.Always), Browsable(True)>
Public Property ImageLayout As DataGridViewImageCellLayout
Get
Return Me.custCellTemplate.ImageLayout
End Get
Set(ByVal value As DataGridViewImageCellLayout)
Me.custCellTemplate.ImageLayout = value
End Set
End Property
Public Overloads Property CellTemplate As AnimatedCell
Get
Return Me.custCellTemplate
End Get
Set(value As AnimatedCell)
Me.custCellTemplate = value
MyBase.CellTemplate = value
End Set
End Property
Public Class AnimatedCell
Inherits System.Windows.Forms.DataGridViewImageCell
Private Animation As Image
Private IsAnimating As Boolean
Public Sub New()
Me.Animation = Nothing
Me.IsAnimating = False
End Sub
Public Overloads Property ImageLayout() As DataGridViewImageCellLayout
Get
Return MyBase.ImageLayout
End Get
Set(ByVal value As DataGridViewImageCellLayout)
MyBase.ImageLayout = value
End Set
End Property
Protected Overrides Sub Paint(graphics As Graphics, clipBounds As Rectangle, cellBounds As Rectangle, rowIndex As Integer, elementState As DataGridViewElementStates, value As Object, formattedValue As Object, errorText As String, cellStyle As DataGridViewCellStyle, advancedBorderStyle As DataGridViewAdvancedBorderStyle, paintParts As DataGridViewPaintParts)
If (IsDBNull(value)) OrElse (value Is Nothing) Then Return
If Me.Animation Is Nothing Then
Me.Animation = CType(formattedValue, Image)
End If
Animate()
ImageAnimator.UpdateFrames()
MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, Nothing, Me.Animation, errorText, cellStyle, advancedBorderStyle, paintParts)
End Sub
Private Sub Animate()
If Me.IsAnimating = True Then Return
If (Me.Animation IsNot Nothing) AndAlso ImageAnimator.CanAnimate(Me.Animation) = True Then
ImageAnimator.Animate(Me.Animation, AddressOf Me.RotateFrame)
Me.IsAnimating = True
End If
End Sub
Private Sub RotateFrame(o As Object, e As EventArgs)
If Me.RowIndex > -1 Then
Me.DataGridView.InvalidateCell(Me)
End If
End Sub
Public Overrides Function Clone() As Object
Dim result As AnimatedCell = New AnimatedCell With {
.IsAnimating = False,
.Animation = Nothing,
.ImageLayout = Me.ImageLayout
}
Return result
End Function
End Class
End Class
这篇关于在 DataGridView 中动画图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!