2

处理计算繁重任务的常用VB方法是将其放入后台工作线程中,而主线程则继续处理UI。Visual Basic.NET:如何创建线程来更新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 
+0

麻烦是我的代码中没有完成很长时间的工作 - 它发生在这个类的客户端中。所以,我无法控制它发生的地方 - 它在主线程中。客户端需要做的是在开始时调用'SetupProgressIndicator'方法,并且不时地调用'IncrementProgressIndicator'方法。可以这样做吗? –

+0

请详细描述情况。很难理解你的意思。 –

+0

请参阅更新谢谢:) –

回答

1

为了清楚地说明问题 - 您需要提供一个可视化组件给客户,他们将在自己的程序中使用它。客户的程序不受您的控制,它将其主要(即:UI)线程绑定在一起,并且您需要在客户端程序被冻结时让您的可视化组件继续工作。

CAN这样做,但这可能不是最优雅或推荐的解决方案。您需要创建第二个应用程序上下文和一个新的消息循环,它可以在主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线程(或其窗体)拥有的任何控件进行交互的调用必须从主UI线程(或其他)用Control.Invoke进行编组,以确保您的新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创建第二个表格。单击Form1上的Button1将锁定Form1,但第二个窗体仍然保持活动状态并响应,每次更新其进度栏Form1时,都会调用.StepTheBar()

在这种情况下,最好的解决方案就是让'客户'学习如何正确编程,避免陷入这个难题。在这种情况下,这是完全不可能的,你必须为它们创建一个组件,尽管代码很差,它仍然会保持运行,那么上述方法可能是唯一的办法。

1

用户界面不能从主事件派发线程以外的任何其他线程更新。所以你正在尝试做一些不起作用的事情。

+0

好的。有没有办法解决另一个可以处理这个问题的进程? –

2

UI只能通过创建它的线程进行更新。任何线程都可以通过使用Control.Invoke方法来请求调用UI线程上的方法,以便它可以更新UI,但这只是等待,直到UI线程不再繁忙为止。如果UI线程繁忙,则用户界面无法被任何人或任何人更新。只有主消息循环(AKA Application.Run)处理队列中的窗口消息并对其执行操作时,UI才会更新。如果UI线程繁忙,陷入循环或等待服务器响应,则无法处理这些窗口消息(除非您致电DoEvents,如果可能的话,我绝对不建议这么做)。所以,是的,当UI很忙时,它会被锁定。这就是为什么大家建议在一个单独的线程中执行任何沉重的业务逻辑的全部原因。如果这不是问题,为什么有人会打扰工作线程?