为了清楚地重申问题 - 您需要向将在他们自己的程序中使用它的客户提供一个可视化组件。客户端的程序,在您的控制之外,占用了它的主(即:UI)线程,并且您需要让您的可视组件在客户端程序被冻结时继续工作。
让我们在这里明确一点 - 客户程序冻结的事实是他们的问题,这是他们的应用程序代码缺陷的直接结果。下面是一个黑客,一个没有人应该使用的可怕的黑客。因此,虽然您可以执行以下操作,但它几乎不应该被视为任何事情的推荐解决方案。它违背了所有最佳实践。
话虽如此,您需要创建第二个应用程序上下文和一个可以在主 UI 线程旁边继续运行的新消息循环。像这样的类会起作用:
Imports System.Threading
Public Class SecondUIClass
Private appCtx As ApplicationContext
Private formStep As Form
Private trd As Thread
Private pgBar As ProgressBar
Delegate Sub dlgStepIt()
Public Sub New()
trd = New Thread(AddressOf NewUIThread)
trd.SetApartmentState(ApartmentState.STA)
trd.IsBackground = True
trd.Start()
End Sub
Private Sub NewUIThread()
formStep = New Form()
pgBar = New ProgressBar()
formStep.Controls.Add(pgBar)
appCtx = New ApplicationContext(formStep)
Application.Run(appCtx)
End Sub
Public Sub StepTheBar()
formStep.Invoke(New dlgStepIt(AddressOf tStepIt))
End Sub
Private Sub tStepIt()
pgBar.PerformStep()
End Sub
End Class
本质上,您对上述类所做的是在新的 STA 线程中创建新的应用程序上下文(为该线程提供消息循环)。该上下文包含一个主表单(赋予它的线程所有权并负责其消息处理),它可以在主 UI 线程之外继续运行。这很像在一个程序中有一个程序——两个 UI 线程,每个线程都有自己的互斥控件集。
与新 UI 线程(或其表单)拥有的任何控件交互的调用必须与Control.Invoke
主 UI 线程(或其他线程)进行编组,以确保您的新 UI 线程是执行交互的线程。你也可以BeginInvoke
在这里使用。
这个类没有清理代码,没有安全检查等(被警告),我什至不确定它会优雅地完成 - 我把这个任务留给你。这只是说明了一种开始的方式。在主窗体中,您将执行以下操作:
Public Class Form1
Private pgClass As New SecondUIClass
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer
For i = 1 To 10
System.Threading.Thread.Sleep(1000)
pgClass.StepTheBar()
Next
End Sub
End Class
运行上面的应用程序将创建Form1
以及由pgClass
. 单击会Button1
在Form1
Form1 通过其循环时锁定它,但第二个表单仍将保持活动状态并做出响应,每次Form1
调用时都会更新其进度条.StepTheBar()
。
实际上,在这种情况下,最好的解决方案是让“客户”学习如何正确编程并避免首先陷入这个难题。在完全不可能的情况下,您必须为他们创建一个组件,尽管他们的代码很糟糕,但该组件仍将保持活动状态,那么上述方法可能是您唯一的办法。