加载 GIF 有时会冻结
我正在尝试在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
“某些 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.
最初,所有事情都发生在单个后台线程中。 总共运行 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!