【问题标题】:VB.net 2010 backgroundworker updating form progressbarVB.net 2010 backgroundworker 更新表单进度条
【发布时间】:2012-08-01 02:54:01
【问题描述】:

试图了解后台工作人员:)

Imports System.Threading
Imports System
Imports System.IO
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Text.RegularExpressions
Imports System.Text
Imports System.Diagnostics
Imports System.Drawing


Public Class CLViewForm

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    BackgroundWorker1.RunWorkerAsync()
End Sub


Function SystemLoads()
    Dim PCV As Integer = Convert.ToDecimal(PerformanceCounter1.NextValue())
    Me.Label5.Text = PCV & " %"
    Me.ProgressBar1.Minimum = 0
    Me.ProgressBar1.Maximum = 100
    Me.ProgressBar1.Step = 1
    Me.ProgressBar1.Value = PCV
    Me.Label6.Text = Now.ToLongTimeString
    Return 0
End Function

Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    Me.Invoke(SystemLoads())
    End Sub
End Class

我正在尝试做的基本要点是加载一个后台工作人员,该工作人员更新主要表单上的 3 个内容 显示当前系统负载的进度条 按百分比显示负载的文本标签 还有一个时钟

我知道我可以用一个简单的计时器来完成所有这些,但是当我添加其他计时器时,它往往会使表单变得迟缓,所以我想学习如何拍摄我想做的大部分事情进入后台线程。

我对这一切都很陌生,所以我不知道如何调用这些东西并对其保持线程安全,因此我收到一个错误 [当然,这就是我在这里问的原因 :)] 说 "vb.net 跨线程操作无效控件'progressbar1' 从一个线程访问,而不是它调用的线程"

或类似的东西。

使用我提供的代码示例如何让后台线程更新表单的进度条和 % 标签?

TIA

【问题讨论】:

    标签: .net vb.net winforms progress-bar backgroundworker


    【解决方案1】:

    我试图在这里创建一个简单的例子。通过使用委托,您在正确的路线上,但我认为您的实施有点偏离。

    您需要一个包含labelprogressbarbackgroundworker 控件的表单,然后将以下代码放入:

    Private Sub TestForm_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        'Start the worker
        BackgroundWorker1.RunWorkerAsync()
    End Sub
    
    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        'simulate long running processes
        UpdateStatus(0, "Loading")
        System.Threading.Thread.Sleep(1000)
        UpdateStatus(33, "One third of the way through")
        System.Threading.Thread.Sleep(1000)
        UpdateStatus(66, "Two thirds of the way through")
        System.Threading.Thread.Sleep(1000)
        UpdateStatus(100, "Finished")
    End Sub
    
    'all calls to update the progress bar and label go through here
    Private Sub UpdateStatus(ByVal progress As Integer, ByVal status As String)
        Try
            If Me.InvokeRequired Then
                Dim cb As New UpdateStatusCallback(AddressOf UpdateStatusDelegate)
                Me.Invoke(cb, New Object() {progress, status})
            Else
                UpdateStatusDelegate(progress, status)
            End If
        Catch ex As Exception
            MessageBox.Show("There was an error " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    
    Private Delegate Sub UpdateStatusCallback(ByVal progress As Integer, ByVal status As String)
    
    'This catually updates the control - modify the paramters and update to suit the control you are using
    Private Sub UpdateStatusDelegate(ByVal progress As Integer, ByVal status As String)
        ProgressBar1.Value = progress
        Label1.Text = status
    End Sub
    

    【讨论】:

    • 谢谢!这对我很有帮助。
    【解决方案2】:

    为后台工作者 RunWorkerCompleted 和 ProgressChanged 创建一个事件。
    C#

    this.backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(/* the event method */);
    this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(/* the event method */);
    

    另外,不要忘记将 WorkerReportsProgress 设置为 true..

    【讨论】:

    • 谢谢你的回复,但是我知道的VB.net很少,我对C#的了解更少,这就是我问vb.net的原因。虽然我知道它们共享许多相同的语法和东西,但我不知道如何将它从 C# 示例“转换”为 VB。我猜你的意思是我需要某种中间工作人员从后台进程中获取信息并使用它将信息代理到前台表单。
    • 如果加载表单时它一直在运行,它是否还需要一个workercompleted?还是我需要在表单关闭事件中输入一些内容,以便它知道杀死后台进程?以及如何开始创建事件?
    • 如果您使用的是microsoft visual basic express,您可以将事件添加到事件窗口。这是一个关于如何使用后台工作者的链接msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx#Y0
    【解决方案3】:

    你需要设置:

    后台工作人员可以使用进度条

    后台工作人员可以报告进度

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
         BW1.RunAsync()
    End Sub
    

    然后:

    Private Sub BW1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BW1.DoWork
    
        Dim i as integer = 1
        Do Until integer = 0
    
            IF BW1.CancellationPending = True then
                 MsgBox("Cancelled by user")
                 Exit Sub
            End If
    
    
             ....
    
    
            BW1.ReportProgress(i)
    
        Loop
    
    End Sub
    

    显示进度:

    Private Sub BW1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BW1.ProgressChanged
         Progressbar1.Value = e.ProgressPercentage  
    End Sub
    

    编辑:

    您不能直接在 DoWork 方法上使用 UI 控件。您需要先委派它们,但为了显示进度(进度条、标签等)Backgroundworker 拥有它自己的方法 (ProgressChanged()),可让您在安全模式下使用 UI。

    【讨论】:

      【解决方案4】:

      进度条有点愚蠢和混乱。我不得不求助于将我所有的工作保持在相同的形式(如果可以的话)。因此,在您的情况下,我可能会尝试在相同形式的过程中执行您的系统加载。这对我有用。 我希望它有所帮助。我粘贴了一些代码,可能会稍微解释一下(无法正确格式化,很抱歉),但简而言之:

      1. 我从 Me.BackgroundWorker.RunWorkerAsync() 开始 工作活动。
      2. 调用BackgroundWorker_DoWork(调用 您想要完成工作并报告的 Proc)
      3. 您的 Proc 完成工作并将使用 BackgroundWorker.ReportProgress 进行报告

      声明

      Public Event DoWork As DoWorkEventHandler
      Public Event PrgRprt As ProgressChangedEventHandler
      Public g_intProgress As Integer             
      

      进度条信息

       Private Sub btnStepOne_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStepTwo.Click
          'Calls  BackgroundWorker_DoWork, which in turn calls step One
          Me.BackgroundWorker1.RunWorkerAsync()
      End Sub
      
      Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker2.DoWork
          'Called from Me.BackgroundWorker1.RunWorkerAsync()
          'calls step One proc, where the work is done
          Call StepOne(strInput)
      End Sub
      
      Private Function StepOne(strInput as string)as string
         'this is where I do my work that the progress bar will monitor   
          Dim x as integer    
          For x = 0 to 100
           'Do your calculations as you loop
            'This is where you report back to the worker to change progress bar   
              Me.BackgroundWorker1.ReportProgress(x)
          Next x
      
      End function
      
      
      Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker2.ProgressChanged
      
          Me.pbConvertion.Value = e.ProgressPercentage
          Me.g_intProgress = e.ProgressPercentage
          Me.pbConvertion.Maximum = "100"
          Me.pbConvertion.Update()
          Me.lblProgbar.Text = "Percentage - " & Me.pbConvertion.Value & "% Completed"
      End Sub
      
      Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
          Me.lblProgbar.Text = "Step One Completed"
      End Sub
      

      【讨论】:

        【解决方案5】:

        我使用的技巧很可怕,但它确实有效:

                    pgbMain.Maximum += 1
                    pgbMain.Value += 2
                    pgbMain.Value -= 1
                    pgbMain.Maximum -= 1
        

        或者你可以使用:

                    i = pgbMain.Value + 1
                    pgbMain.Value = pgbMain.Maximum
                    pgbMain.Value = i
        

        但后一种方法意味着最后一步像往常一样延迟,因为没有“后退”步骤。 (不,这两种方法都无法在视觉上检测到。)

        您可以在手动调整进度条 Value 属性时在 WinForm 上看到,它会逐渐移动到较高的值,但会立即移动到较低的值。所以这似乎摆脱了滞后。很遗憾。

        【讨论】:

          【解决方案6】:

          已经为您提供了更好的解决方案,但我使用以下更改来使您的想法生效:

          1. 正如您在评论中所问的那样,BackgroundWorker 只执行一次DoWork 事件。所以在RunWorkerCompleted 事件中再次调用RunWorkerAsync
          2. VB.NET一般只用AddressOf的时候就提供delegate,但是Control.Invoke太笼统了,只好指定一个。但是,该框架提供了MethodInvoker。因此,将 SystemLoads 更改为 Sub
          3. 一些“百分比”PerfomaceCounters 似乎不遵守0<=%<=100

          因此,让您克服当前障碍的简单更改为您提供:

          Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
              BackgroundWorker1.RunWorkerAsync()
          End Sub
          
          Sub SystemLoads()
              Dim PCV As Integer = Convert.ToInt32(PerformanceCounter1.NextValue())
              Me.Label5.Text = PCV & " %"
              Me.ProgressBar1.Minimum = 0
              Me.ProgressBar1.Maximum = 100
              Me.ProgressBar1.Step = 1
              Me.ProgressBar1.Value = Math.Min(Math.Max(0, PCV), 100)
              Me.Label6.Text = Now.ToLongTimeString
          End Sub
          
          Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
              Me.Invoke(New Methodinvoker(AddressOf SystemLoads))
          End Sub
          
          Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
              BackgroundWorker1.RunWorkerAsync()
          End Sub
          

          这导致了我在LinqPad query 中解决的其他一些问题,具体来说:

          1. 关闭窗口时总是收到ObjectDisposedException。我尝试减轻它,但最后,当Form 已被处理时,我只是忽略了它。
          2. 否则会出现例外情况。
          3. Sleep 稍微减少更新,以减少调整表单大小时的迟缓。这表明Timer 会更好。
          4. 即使这里不需要,也要使用正确的If InvokeRequired 模式。

          注意,我必须制作自己的控制位置,而且它们几乎不能正常工作。 此外,您必须将 PerformanceCounter 至少更改为系统上存在的 InstanceName

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多