简体   繁体   English

VB.net 2010 Backgroundworker更新表单进度栏

[英]VB.net 2010 backgroundworker updating form progressbar

Trying to understand background workers :) 试图了解背景工作者:)

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

The basic gist of what I am trying to do is just load a background worker that updates 3 things on the primary form a progressbar that shows current system load a text label that shows the load by percent and a clock 我要执行的操作的基本要点是加载后台工作程序,该工作程序会在主窗体上更新3项内容,并在进度条上显示当前系统的负载,并显示一个文本标签,以百分比和时钟的形式显示负载

I know I can do all this with a simple timer, but when I go to add my other timers it tends to make the form all kinds of sluggish so I wish to learn how to shoot most of these things I want to do into a background thread. 我知道我可以用一个简单的计时器来完成所有这些工作,但是当我添加其他计时器时,它往往会使各种形式的反应迟钝,因此我想学习如何将大部分我想做的事情拍摄到背景中线。

I am new to all this so I do not know how to call these things and be thread safe about it and as such I get an error [of course, which is why I am here asking :)] that says "vb.net cross-thread operation not valid control 'progressbar1' accessed from a thread other than which is it called" 我对所有这些都是新手,所以我不知道如何调用这些东西并对此保持线程安全,因此我得到了一个错误[当然,这就是为什么我在这里问:)]说“ vb.net cross -thread操作不是从所谓的其他线程访问的有效控件“ progressbar1”

or something to that effect. 或类似的东西。

Using the code sample I supplied how can I make the background thread update the form's progress bar and % label? 使用我提供的代码示例,如何使后台线程更新表单的进度栏和%标签?

TIA TIA

I have tried to create a simple example here. 我试图在这里创建一个简单的例子。 You are on the right lines by using a delegate but I think your inplementation is a little off. 通过使用委托,您处于正确的位置,但是我认为您的充实度有点差。

A form that has a label , a progressbar and a backgroundworker control on it is what you need then drop the following code in: 您需要的是具有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

Create an event for the background worker RunWorkerCompleted and ProgressChanged.. 为后台工作程序RunWorkerCompleted和ProgressChanged创建一个事件。
C# C#

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

Also, do not forget to set the WorkerReportsProgress to true.. 另外,不要忘记将WorkerReportsProgress设置为true。

You need to set: 您需要设置:

Backgroundworker can use progressbar Backgroundworker可以使用进度条

Backgroundworker can report progress Backgroundworker可以报告进度

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

Then: 然后:

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

Show the progress: 显示进度:

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

EDIT: 编辑:

You can't use UI Controls directly on DoWork method. 您不能直接在DoWork方法上使用UI控件。 You need to delegate them first but for showing progress (progressbar, label, etc) Backgroundworker has it owns method (ProgressChanged()) that let you use the UI in a safe mode. 您需要首先委派它们,但要显示进度(进度条,标签等),Backgroundworker拥有自己的方法(ProgressChanged()),可让您在安全模式下使用UI。

Progress bar is kinda goofy and confusing. 进度栏有点愚蠢和混乱。 I had to resort to keeping all my work in the same form (if you can). 我不得不求助于所有工作保持相同的形式(如果可以的话)。 So in your case I might try doing your systemloads inside a procedure in the same form. 因此,在您的情况下,我可能会尝试以相同的形式在过程中进行系统加载。 That's what worked for me. 那对我有用。 I hope it helps. 希望对您有所帮助。 I have pasted some code that might explain it a bit (can't get it to format right in SO- sorry) but in a nutshell: 我粘贴了一些可能会对其进行解释的代码(抱歉,无法正确格式化),但总而言之:

  1. I start with Me.BackgroundWorker.RunWorkerAsync() Which starts the Do-work event. 我从开始执行工作事件的Me.BackgroundWorker.RunWorkerAsync()开始。
  2. that calls BackgroundWorker_DoWork (which calls the Proc you want to do the work and report back) 调用BackgroundWorker_DoWork(调用要执行工作的Proc并进行报告)
  3. Your Proc that does the work and will report back using BackgroundWorker.ReportProgress 完成工作并使用BackgroundWorker.ReportProgress进行报告的Proc

Declaration 宣言

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

progress bar info 进度条信息

 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

The trick I use is dreadful but it works: 我使用的技巧很可怕,但是有效:

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

Or you could use: 或者您可以使用:

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

But the latter method means that the final step is as delayed as usual as there is no 'backward' step. 但是,后一种方法意味着最后一步像没有“后退”步骤一样,像通常一样延迟。 (No, neither method is visually detectable.) (不,这两种方法都无法在视觉上检测到。)

You can see on the WinForm when adjusting the progress bar Value property manually that it gradually moves to higher values but instantly moves to lower ones. 您可以在WinForm上手动调整进度条的Value属性时看到它逐渐移到较高的值,但立即移到较低的值。 So this seems to get rid of the lag. 因此,这似乎摆脱了滞后。 Sadly. 可悲的是。

You have been provided better solutions, but here are the changes I use to get your idea working: 为您提供了更好的解决方案,但是以下是为了使您的想法可行而进行的更改:

  1. As you asked in a comment, the BackgroundWorker only executes the DoWork event once. 如您在注释中所要求的, BackgroundWorker仅执行一次DoWork事件。 So call RunWorkerAsync again in RunWorkerCompleted event. 因此,在RunWorkerCompleted事件中再次调用RunWorkerAsync
  2. VB.NET normally provides the delegate when you just use AddressOf , but Control.Invoke is too general, so you have to specify one. 当您仅使用AddressOf ,VB.NET通常提供委托,但是Control.Invoke太笼统了,因此您必须指定一个。 However, the framework provides MethodInvoker . 但是,该框架提供了MethodInvoker Thus, change SystemLoads to a Sub . 因此,将SystemLoads更改为Sub
  3. Some of the "Percent" PerfomaceCounters don't seem to adhere to 0<=%<=100 . 一些“百分比” PerfomaceCounters似乎不遵循0<=%<=100

So the simple changes to get you past the current roadblocks give you: 因此,通过简单的更改即可摆脱当前的障碍,从而为您提供:

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

This leads to a few other issues that I've addressed in this LinqPad query , specifically: 这会导致我在此LinqPad查询中解决的其他一些问题,特别是:

  1. Always getting an ObjectDisposedException on closing the window. 在关闭窗口时始终会得到ObjectDisposedException I tried mitigating it, but, in the end, I just ignored it when the Form has been disposed. 我尝试缓解它,但是最后,当处理完Form ,我只是忽略了它。
  2. Otherwise catering for exceptions. 否则迎合例外。
  3. Sleep a bit to reduce the updates, to reduce sluggishness when resizing the form. 稍微Sleep以减少更新,以减少调整大小时的迟缓。 This is where it shows a Timer would be better. 这表明Timer会更好。
  4. Use the proper If InvokeRequired pattern even if it's not needed here. 使用正确的If InvokeRequired模式,即使此处不需要。

NB I had to fabricate my own control locations and they're barely OK as it stands. 注意:我必须制造自己的控制位置,但就目前情况而言,它们几乎没有问题。 Also, you'll have to change the PerformanceCounter to at least an InstanceName that exists on your system. 另外,您还必须将PerformanceCounter至少更改为系统上存在的InstanceName

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM