加载 GIF 有时会冻结

发布于 2024-07-14 03:06:07 字数 2642 浏览 8 评论 0原文

我正在尝试在 Windows 窗体顶部使用加载覆盖层,在带有加载 GIF 的 Windows 窗体顶部添加 50% 不透明层,同时它在后台线程中执行需要执行的操作。 覆盖层是我在 onPaint 事件中绘制的窗口窗体。

加载覆盖在多个不同的表单加载上工作正常,但在 30 秒的上传过程(将 Word 文档打印为 PDF,然后将该 PDF 上传到 SQL Server)期间被调用以确保耐心时无法正常工作。 此上传从表单收集一些数据,将其放入对象中,然后完全在后台线程上运行。 将出现加载叠加层,显示加载 GIF 的第一帧,然后冻结。 onPaint 被触发并且图像帧正在更新,但它不可见

构造函数将表单设置为 UserPainted:

Sub New()
    InitializeComponent()
    SetStyle(ControlStyles.UserPaint Or ControlStyles.Opaque, True) 
End Sub

然后,在 Form.Shown 事件中调用 ImageAnimator.Animate 方法:

Private Sub LoadingOverlay_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
    If Not currentlyAnimating Then
        ImageAnimator.Animate(animatedImage, AddressOf Me.OnFrameChanged)
        currentlyAnimating = True
    End If
End Sub

onFrameChanged 事件处理程序只是使form:

Private Sub OnFrameChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    'Force a call to onPaint
    Me.Invalidate()
End Sub

然后覆盖 onPaint 并进行绘制:

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    MyBase.OnPaint(e)

    'Get the next frame ready for rendering
    ImageAnimator.UpdateFrames()

    'Draw the next frame in the animation.
    e.Graphics.DrawImage(Me.animatedImage, GetCenter(Me.animatedImage.Size))
    TextRenderer.DrawText(e.Graphics, strStatus, Me.Font, GetTextLocation(Me.animatedImage.Size), Color.White, Color.Black)

End Sub

最后,在 Form Closing 事件中调用 ImageAnimator.StopAnimate 方法:

Private Sub LoadingOverlay_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
    If currentlyAnimating Then
        ImageAnimator.StopAnimate(animatedImage, AddressOf Me.OnFrameChanged)
        currentlyAnimating = False
    End If
End Sub

以下是调用和关闭加载叠加层的方式:

Private Sub ShowLoadingOverlay()
    If Not blnLoadingOverlayVisible Then

        Me.Enabled = False

        patience = New LoadingOverlay()
        patience.Location = Point.Add(parent.PointToScreen(Me.Location), New Size(0, parent.ToolStrip.Height + parent.MenuStrip.Height))
        patience.Size = Me.Size
        patience.Show()
        patience.BringToFront()

        blnLoadingOverlayVisible = True

    End If
End Sub

Private Sub HideLoadingOverlay()
    If blnLoadingOverlayVisible Then
        'Close loading overlay'
        patience.Close()
        patience.Dispose()
        patience = Nothing
        Me.Enabled = True
        blnLoadingOverlayVisible = False
    End If
End Sub

I'm trying to use a loading overlay on top of a Windows form that adds a 50% opaque layer on top of a windows form with a loading GIF, while it does what it needs to do in a background thread. The overlay is a windows form that I'm drawing in the onPaint event.

The loading overlay works fine on multiple different form loads, but fails to work properly when summoned to ensure patience during a 30 second upload process (That Prints a Word Document to PDF, then uploads that PDF to a SQL Server). This upload collects some data from the form, puts it into an Object, then operates entirely on a background thread. The loading overlay will appear, show the first frame loading GIF, then just freeze. The onPaint is getting fired and the image frame is being updated, but it isn't visible

Constructor sets the form to be UserPainted:

Sub New()
    InitializeComponent()
    SetStyle(ControlStyles.UserPaint Or ControlStyles.Opaque, True) 
End Sub

Then, in the Form.Shown event the ImageAnimator.Animate method is called:

Private Sub LoadingOverlay_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
    If Not currentlyAnimating Then
        ImageAnimator.Animate(animatedImage, AddressOf Me.OnFrameChanged)
        currentlyAnimating = True
    End If
End Sub

The onFrameChanged Event Handler just Invalidates the form:

Private Sub OnFrameChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    'Force a call to onPaint
    Me.Invalidate()
End Sub

Then onPaint is overridden and does the drawing:

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    MyBase.OnPaint(e)

    'Get the next frame ready for rendering
    ImageAnimator.UpdateFrames()

    'Draw the next frame in the animation.
    e.Graphics.DrawImage(Me.animatedImage, GetCenter(Me.animatedImage.Size))
    TextRenderer.DrawText(e.Graphics, strStatus, Me.Font, GetTextLocation(Me.animatedImage.Size), Color.White, Color.Black)

End Sub

Finally, the ImageAnimator.StopAnimate method is called in the Form Closing event:

Private Sub LoadingOverlay_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
    If currentlyAnimating Then
        ImageAnimator.StopAnimate(animatedImage, AddressOf Me.OnFrameChanged)
        currentlyAnimating = False
    End If
End Sub

Here's how the loading overlay is being called and closed:

Private Sub ShowLoadingOverlay()
    If Not blnLoadingOverlayVisible Then

        Me.Enabled = False

        patience = New LoadingOverlay()
        patience.Location = Point.Add(parent.PointToScreen(Me.Location), New Size(0, parent.ToolStrip.Height + parent.MenuStrip.Height))
        patience.Size = Me.Size
        patience.Show()
        patience.BringToFront()

        blnLoadingOverlayVisible = True

    End If
End Sub

Private Sub HideLoadingOverlay()
    If blnLoadingOverlayVisible Then
        'Close loading overlay'
        patience.Close()
        patience.Dispose()
        patience = Nothing
        Me.Enabled = True
        blnLoadingOverlayVisible = False
    End If
End Sub

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

時窥 2024-07-21 03:06:07

“某些 COM”是相关的。 您将会遇到 COM 的公寓线程规则。 必须在 STA 线程上创建 COM 对象(例如 Word)。 您的主 UI 线程符合条件,它从 Main() 开始,并且具有 [STAThread] 属性。 您在另一个线程上进行的任何方法调用都会由 COM 自动封送至 STA 线程。 给你的动画涂胶。

这并不容易解决。 您需要一个后台线程,该线程也必须是 STA 线程,请使用 Thread.SetApartmentState()。 并泵送消息循环,使用Application.Run()。 启动代码和退出循环很尴尬,请尝试使用重写 SetVisibleCore() 的表单,这样就可以避免使其可见。

"some COM" is relevant. You'll hit COM's apartment threading rulez. A COM object like Word must be created on an STA thread. Your main UI thread qualifies, it starts at Main() and it has the [STAThread] attribute. Any method calls you make on another thread are automatically marshaled by COM to the STA thread. Gumming up your animation.

This isn't easy to fix. You'd need a background thread that must be an STA thread as well, use Thread.SetApartmentState(). And pump a message loop, use Application.Run(). Getting your code started and exiting the loop is awkward, try using a form that overrides SetVisibleCore() so you can avoid making it visible.

演多会厌 2024-07-21 03:06:07

最初,所有事情都发生在单个后台线程中。 总共运行 3 个方法。 当我将上传线程(大约完成 1/2 的工作)移至另一个后台线程时,一切都“正常”(不完美,GIF 在开始时仍然会跳一点)。

移至第二个后台线程的上传方法根本不与 GUI 线程交互,它只执行一些 COM(字)和 SQL 操作。 长话短说,它有效,但我仍然不知道为什么。 任何见解将不胜感激!

Initially everything is happening in a single background thread. There are 3 methods total that run. When I moved the upload thread (which does about 1/2 the work) into another background thread, everything is "ok" (not perfect, GIF still jumps a little at the beginning).

The upload method that was moved into a second background thread doesn't interact with the GUI thread at all, it just does some COM (word) and SQL stuff. Long story short, it's working, but I still have no idea why. Any insight would be greatly appreciated!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文