[英]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: 您需要的是具有label
, progressbar
和backgroundworker
控件的表单,然后将以下代码放入:
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: 我粘贴了一些可能会对其进行解释的代码(抱歉,无法正确格式化),但总而言之:
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: 为您提供了更好的解决方案,但是以下是为了使您的想法可行而进行的更改:
BackgroundWorker
only executes the DoWork
event once. 如您在注释中所要求的, BackgroundWorker
仅执行一次DoWork
事件。 So call RunWorkerAsync
again in RunWorkerCompleted
event. 因此,在RunWorkerCompleted
事件中再次调用RunWorkerAsync
。 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
。 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查询中解决的其他一些问题,特别是:
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
,我只是忽略了它。 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
会更好。 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.