-->

Visual Basic.NET中:如何创建一个线程来更新UI(Visual Basic.NET:

2019-06-25 16:16发布

通常的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

Answer 1:

为了清楚地重申这个问题 - 你需要提供一个可视化组件的客户,谁就会自己程序中使用它。 客户端的程序,你的控制之外,挤占其主要(即: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 。 点击Button1Form1会锁定Form1的同时通过其循环去,但第二种形式将仍然活着,反应灵敏,每次更新其进度条Form1称为.StepTheBar()

在这种情况下,最好的解决办法,真的,是让“客户”学习如何正确地进行编程,并避免被卡在这个难题摆在首位。 在这是完全不可能的,你必须为他们创建一个组件,尽管他们穷代码界河仍将活着的话,那么以上方法可能是你唯一的办法。



Answer 2:

该UI只能通过创建它的线程进行更新。 任何线程可以调用UI线程上的方法,以便它可以更新用户界面,通过使用请求Control.Invoke方法,但这样做只是坐着等待,直到UI线程不再忙。 如果UI线程繁忙,用户界面不能被任何东西或任何更新。 用户界面仅被更新时,主消息循环(AKA Application.Run )处理在队列中的窗口消息并且在他们的行为。 如果UI线程繁忙,死循环或等待来自服务器的响应,它不能处理这些窗口消息(除非你调用DoEvents ,这我绝对不建议,如果可能的话)。 所以,是的,而UI是忙碌的,它会被关起来。 这就是全部的原因,大家都建议做任何沉重的业务逻辑在一个单独的线程。 如果这不是一个问题,为什么会有人懒得做的工作线程?



Answer 3:

该UI不能从任何其他线程比主事件分派线程更新。 所以,你要做的东西,将无法正常工作。



文章来源: Visual Basic.NET: how to create a thread to update the UI