【问题标题】:UI update and delegate in another class doesnt work in separate thread另一个类中的 UI 更新和委托在单独的线程中不起作用
【发布时间】:2017-02-07 21:27:37
【问题描述】:

为了保持 UI 的响应性,我使用单独的线程来执行各种进程,例如一些 FTP 下载。

Private Sub Button11_Click(sender As Object, e As EventArgs) Handles Button11.Click
    Dim ThreadResync As System.Threading.Thread
    ThreadResync = New System.Threading.Thread(AddressOf Bodacc_ResyncFTP)
    ThreadResync.Start()
End Sub

Sub Bodacc_ResyncFTP()
    Dim MyBodacc As bodacc_data = New bodacc_data
    MyBodacc.Label_Status = Form1.Label1
    MyBodacc.ResyncFTP()
End Sub

使用线程更新 UI 的一种方法是 Delegate 东西,所以在 bodacc_data 中我不得不

Public Class bodacc_data
    ......
    Delegate Sub UpdateLabelDelg(text As String, ThisLabel As Label)
    Public Delegate_label As UpdateLabelDelg = New UpdateLabelDelg(AddressOf set_label)
    Public Label_Status = Label
    ......
    Sub set_label(stext As String, ThisLabel As Label)
        ThisLabel.Text = stext
    End Sub
    .....

    Sub ResyncFTP()
        //says hello
        If Label_Status.InvokeRequired = True Then
            Label_Status.Invoke(Delegate_label, New Object() {"Working...", Label_Status})
        Else
            Label_Status.Text = "Working..."
        End If

        //do stuff
    End Sub
End Class

它就像一个魅力。但是我有很多课程或多或少都在做相同的事情(磁盘更新、数据库更新、FTP 更新)并且必须复制/过去所有委托/外部标签声明/迷你子/调用听起来很傻。

所以我创建了一个类来处理这些 UI 更新/委托,以便快速访问

Public Class Form_UI

    Delegate Sub UpdateLabelDelg(text As String, ThisLabel As Label)
    Public Delegate_label As UpdateLabelDelg = New UpdateLabelDelg(AddressOf set_label)

    Private Labels(2) As Label

    Sub New()

        Labels(0) = Form1.Label1
        Labels(1) = Form1.Label2
        Labels(2) = Form1.Label3

    End Sub

    Sub set_label(stext As String, ThisLabel As Label)
        ThisLabel.Text = stext
    End Sub


    Public Sub ChangeLabel(ByVal LabelNum As Integer, nText As String)
        LabelNum = LabelNum - 1

        If Labels(LabelNum).InvokeRequired Then
            Labels(LabelNum).Invoke(Delegate_label, New Object() {nText, Labels(LabelNum)})
        Else
            Labels(LabelNum).Text = nText
            Labels(LabelNum).Update()
        End If

    End Sub
End Class

所以,现在在改进后的bodacc_data 和所有其他处理类中我只有:

Public Class bodacc_data
    ......
    Private MyUI as Form_UI
    .....
    Sub New()
        MyUI = New Form_UI()
    End Sub

    Sub ResyncFTP()
        //says hello
        MyUI.ChangeLabel(1, "Working...")

        //do stuff
    End Sub
End Class

问题为什么在线程中调用ResyncFTPMyUI.ChangeLabel 不更新,但在主线程中调用时有效(如下面的代码示例)

Private Sub Button11_Click(sender As Object, e As EventArgs) Handles Button11.Click
    Dim MyBodacc As bodacc_data = New bodacc_data 
    MyBodacc.ResyncFTP()
End Sub

请注意,不会引发错误。值得注意的奇怪之处在于<Form_UI>.ChangeLabel() 从来没有走.Invoke 路线,而是正常的更新路线。我强烈怀疑存在范围问题或洞察力问题。

【问题讨论】:

    标签: vb.net multithreading user-interface scope


    【解决方案1】:

    当您创建 Windows 窗体应用程序时,您会设置一个 UI 线程,该线程旨在成为所有 UI 的所有者。 UI 线程包含用于更新所有 UI 的消息泵。

    但是您在Button11_Click 中所做的是创建一个新线程,该线程调用Dim MyBodacc As bodacc_data = New bodacc_data,而后者又调用MyUI = New Form_UI()

    您正在非 UI 线程上创建表单。没有消息泵,因此 UI 不会更新。

    【讨论】:

    • 我明白了,按照你的解释,我在form_load中更改了New bodacc_dataForm_UI,所以它们是在UI线程中创建的。单独的线程现在在已经存在的MyBodacc.ResyncFTP() 中调用并且它可以工作。感谢您的洞察力!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    相关资源
    最近更新 更多