从后台工作人员调用的另一个 class 更新标签和 ProgressBar 值

Update Label And ProgressBar value from another class invoked in a background worker

你好,我在主窗体的 StatusStrip 中更新进度条和标签时遇到问题。

StatusStrip 内的表单中有 2 个控件:

基本上我的情况是这样的:

Public Class Main
    Public Sub TEST(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles TEST.DoWork
          Dim tmp as New NAMESPACE1.CLASS2(VALUES)
    End Sub 
End Class

Namespace NAMESPACE1
    Public Class CLASS2
         Public Sub New(VALUES)
             Main.Progressbar.Value = 15
             Main.ProgressLabel.Text = "hello!"
         End Sub
     End Class 
End Namespace

问题是控件的文本或值在代码中更新(我看到它使用断点),但不是在进度条始终为 0% 且标签始终为无的形式中。

我认为是主窗体的更新或刷新问题。我试过 Main.Refresh() 和 Main.Update() 但不管怎样都行不通。

提前致谢。

这是一个按照 Plutonix 的建议传递对 MAIN 的引用的示例。我故意让你的 伪代码 风格完好无损:

Public Class MAIN

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        TEST.RunWorkerAsync()
    End Sub

    Private Sub TEST_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles TEST.DoWork
        Dim tmp As New NAMESPACE1.CLASS2(Me, VALUES) ' <-- Form reference being passed via 1st parameter
    End Sub

End Class

Namespace NAMESPACE1

    Public Class CLASS2

        Private frmMain As MAIN

        Public Sub New(ByVal frmMain As MAIN, VALUES)
            Me.frmMain = frmMain

            Me.frmMain.Progressbar.Value = 15
            Me.frmMain.ProgressLabel.Text = "hello!"
        End Sub

    End Class

End Namespace

您有 2 个问题在进行中。首先是 Main 是一个 class 名称,而不是运行时引用或对象变量。请参阅 Idle_Mind 关于使用 Me 获取运行时对象引用的回答。

第二个问题是,由于Class2是在DoWork中创建的,它是在后台线程上创建的,这将阻止它访问UI控件(创建于UI 线程)。你会得到一个非法的跨线程操作异常(即使你没有看到它)。

我建议 Class2 不会做任何使用 ReportProgress 方法无法完成的有用的事情。摆脱它也摆脱了表单引用问题,因为事件是在与 UI 控件相同的线程上引发的:

Private WithEvents bgw As BackgroundWorker

...
' in a button click or whatever starts the worker:
bgw = New BackgroundWorker
bgw.WorkerReportsProgress = True
bgw.RunWorkerAsync(5)         ' times to loop

...
Private Sub bgw_DoWork(sender As Object, 
           e As DoWorkEventArgs) Handles bgw.DoWork

    ' NOTE
    ' This code executes on a different thread
    '   so do not reference UI controls!

    ' e.Argument is the value passed - amount of work
    Dim max As Integer = CInt(e.Argument)

    For n As Integer = 1 To max
        Threading.Thread.Sleep(250)     ' emulates work
        ' causes the ProgressChanged event to fire:
        bgw.ReportProgress(n, String.Format("{0} of {1}", n.ToString, max.ToString))
    Next

End Sub

Private Sub bgw_ProgressChanged(sender As Object, 
                e As ProgressChangedEventArgs) Handles bgw.ProgressChanged
    'ProgressChanged fires on the UI thread, so it is safe to 
    ' referenece controls here
    TextBox4.Text = e.UserState.ToString
    TextBox4.Refresh()
End Sub

粘贴代码,您可以在TextBox中看到消息的变化。使用您的 ProgressBarProgressLabel.

也是一样的
bgw.ReportProgress(n, arg)

第一个参数将映射到 ProgressChanged 事件中的 e.ProgressPercentage。第二个是可选的 - UserState。我用它来传递一个字符串用于说明目的(表格已经可以知道工作量,因为它告诉 BGW 要做什么。)


如果Class2有其他用途,只要在UI线程上创建(在表单中)并在该线程上使用(即在ProgressChanged 事件)。您还需要一种与控件对话的方法,这样您就不必每次都创建一个新控件:

Private myObj As Class2           ' declaration
...
myObj = New Class2(Me)             ' instance with frm ref

在class2:

Public Sub Update(value As Integer, msg As String)
   frmMain.Progressbar.Value = value
   frmMain.ProgressLabel.Text = msg
End Sub

然后在 ProgressChanged 事件中:

myObj.Update(x, y)

其中 xy 是来自任何地方的值和消息。