通常的VB的方式来处理计算任务重是把它在后台工作线程,而主线程保持处理UI。
说不管是什么原因,我需要周围做这个的其他方式:主线程做繁重的工作和一个后台更新UI。
这是我到目前为止所。 唯一的问题是,虽然UI窗口(Form1上)不得到重绘,你不能与它进行交互,甚至无法移动或调整其大小(鼠标光标变为沙漏,不会点击)。
Public Class ProgressDisplay
Private trd As Thread
Public Sub New()
trd = New Thread(AddressOf threadtask)
trd.Start()
End Sub
Private Sub threadtask()
Dim f1 As Form1
f1 = New Form1
f1.Show()
Do
f1.Update()
Thread.Sleep(100)
Loop
End Sub
End Class
编辑:理想情况下,我需要呈现的界面像这样的客户端
Public Class ProgressDisplay
Public Sub New()
Public Sub Update(byval progress as int)
End Class
客户端将这样称呼它(实际上是在非托管C ++了COM,但你得到的图片):
Dim prog = new ProgressDisplay()
DoLotsOfWork(addressof prog.update) ' DoLotsOfWork method takes a callback argument to keep client informed of progress
为了清楚地重申这个问题 - 你需要提供一个可视化组件的客户,谁就会自己程序中使用它。 客户端的程序,你的控制之外,挤占其主要(即: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
这里。
这个类有NO清除代码,不检查安全等(警告),我甚至不知道它会优雅地完成了 - 我这个任务留给你。 这正好说明了上手的方式。 在主窗体,你会做这样的事情:
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()
在这种情况下,最好的解决办法,真的,是让“客户”学习如何正确地进行编程,并避免被卡在这个难题摆在首位。 在这是完全不可能的,你必须为他们创建一个组件,尽管他们穷代码界河仍将活着的话,那么以上方法可能是你唯一的办法。
该UI只能通过创建它的线程进行更新。 任何线程可以调用UI线程上的方法,以便它可以更新用户界面,通过使用请求Control.Invoke
方法,但这样做只是坐着等待,直到UI线程不再忙。 如果UI线程繁忙,用户界面不能被任何东西或任何更新。 用户界面仅被更新时,主消息循环(AKA Application.Run
)处理在队列中的窗口消息并且在他们的行为。 如果UI线程繁忙,死循环或等待来自服务器的响应,它不能处理这些窗口消息(除非你调用DoEvents
,这我绝对不建议,如果可能的话)。 所以,是的,而UI是忙碌的,它会被关起来。 这就是全部的原因,大家都建议做任何沉重的业务逻辑在一个单独的线程。 如果这不是一个问题,为什么会有人懒得做的工作线程?
该UI不能从任何其他线程比主事件分派线程更新。 所以,你要做的东西,将无法正常工作。