winforms 在DataGridView中动画显示图像

hfsqlsce  于 2022-12-14  发布在  其他
关注(0)|答案(1)|浏览(183)

我编写了下面的helper类,以便在DataGridView中动画化图像,但它不起作用(图像没有动画化)。
在此之前,我在wen上找到了一些示例代码,但它们也不起作用。
我想了解这是如何工作的,而不是仅仅因为我的应用程序能工作就把一段代码塞进去。为什么我的代码不能做它预期的事情?

编辑

我发现了它不工作的原因。源代码DataTable本身不包含图像:它们通过CellFormatting处理方法分配给DataGridView的Cells。由于该事件也一直触发,因此总是传递一个新鲜的图像对象,因此它总是显示图像的第一帧。当我创建一个新列并将原生图像值存储在其中时,它们按照需要进行动画处理。
现在的问题是是否可以在DataGridViewCellFormatting事件处理程序方法中为分配给.FormattedValue属性的图像设置动画?

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
qlckcl4x

qlckcl4x1#

带有自定义单元格的自定义列具有一些优点。
所有的设计逻辑都被限制在一个地方,可以在设计时使用DataGridView设计器将其选作Column模板。

性能是相当不错的(测试了200个动画细胞),我没有注意到任何 Flink 。
可以使用设计器设置、通过代码或手动调整行/列的大小,像往常一样拉伸或缩放动画Gif。

然而,我不能认为它是完整的,因为我找不到一个好的方法来启动所有的动画使用这个自定义的列类的方法或属性。

编辑:

已将扩展方法添加到DataGridViewDataGridView.Animate())。
这允许 * 隐藏 * 无效过程。
DataGridView数据绑定完成后,只需调用扩展方法:

DataGridView1.DataSource = [DataSource]
DataGridView1.Animate()

包含Extension方法的模块:

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

当然,这还不够好,还需要更多的研究。
这是自定义动画列类:

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

相关问题