I have a background worker that is supposed to be updating a ToolStripLabel with some status messages. However, the updating is not happening, but no errors are being thrown. Here's the code I am using:
Private Sub BackgroundWorker3_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker3.DoWork
BackgroundWorker3.WorkerReportsProgress = True
Dim Counter As Integer = 0
Do Until BW1Running = False
Counter = Counter + 1
Threading.Thread.Sleep(1000)
Incident_Form.BackgroundWorker3.ReportProgress(Counter)
If Counter >= 100 Then
e.Result = False
Return
End If
Loop
If BW1Running = False Then
Counter = 100
Incident_Form.BackgroundWorker3.ReportProgress(Counter)
End If
End Sub
Private Sub BackgroundWorker3_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker3.ProgressChanged
Me.ToolStripStatusLabel1.Text = e.ProgressPercentage.ToString
End Sub
Nothing happens when the ProgressChanged is fired. I've debugged it and it'll print a line to the output window, but it will not update that label. Any ideas on what I'm missing?
You're calling:
Incident_Form.BackgroundWorker3.ReportProgress()
instead of just:
BackgroundWorker3.ReportProgress()
Your BackgroundWorker3_ProgressChanged method is subscribed to the ProgressChanged event of the BackgroundWorker located in the current form, not in the Incident_Form form.
Remove Incident_Form from the beginning of the BackgroundWorker3.ReportProgress() calls and you should be good to go.
Related
I have an infinite loop in this sub because I want the program to keep testing this process to see if the variable has changed. When I run the program in the debugger, nothing shows up, including the form however when I removed the infinite loop from the program, the form showed up again. Does anyone know why this is happening? I should also mention I've tried a DO LOOP as well. Can anyone help?
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim greenCount As Integer
Dim looptest As Boolean = True
While looptest = True
For Each control In Me.Controls.OfType(Of Button)
If control.BackColor = Color.Lime Then
greenCount += 1
End If
Next
txtFielder.Text = greenCount.ToString
End While
End Sub
You need to get rid of all that code regardless. Depending on how you're changing the BackColor of those Buttons in the first place, updating the lime count might be best done there. Otherwise, you should be handling the appropriate event, e.g.
Private limeButtonCount As Integer = 0
Private Sub Buttons_BackColorChanged(sender As Object, e As EventArgs) Handles Button3.BackColorChanged,
Button2.BackColorChanged,
Button1.BackColorChanged
If DirectCast(sender, Button).BackColor = Color.Lime Then
limeButtonCount += 1
Else
limeButtonCount -= 1
End If
TextBox1.Text = limeButtonCount.ToString()
End Sub
Note that this code assumes that there are only two possible BackColor values and that all Buttons are not lime by default. If your scenario is a bit more complex than that then you may need to change a code a little, e.g.
Private limeButtonCount As Integer = 0
Private Sub Buttons_BackColorChanged(sender As Object, e As EventArgs) Handles Button3.BackColorChanged,
Button2.BackColorChanged,
Button1.BackColorChanged
limeButtonCount = Controls.OfType(Of Button)().Count(Function(b) b.BackColor = Color.Lime)
TextBox1.Text = limeButtonCount.ToString()
End Sub
Form.Load occurs before a form is displayed for the first time.
This means that you'll never see your form as long as you loop in this event. You probably want to use the Shown event instead.
I'm new with multithread and backgroundworker.
I'm tried to write a simple test based upon tutorials and forum threads, but it is not working how I expected.
I have a windows form appilcation with Button1, Label1, BackgroundWorker1 controls.
I'd like to do that when I click on the button:
Backgroundworker check continously if a variable state True or False
First change label text to False (first time state = false)
if state is set True in the main thread, change label text to True (state is set True when the "Sample process" (For Next) ends)
My code is:
Public state As Boolean
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Debug.Print("Button1_Click ThreadID - " & Thread.CurrentThread.ManagedThreadId)
state = False
BackgroundWorker1.RunWorkerAsync()
'--Sample process in the main thread
For c = 0 To 10000
Debug.Print(c)
Next
state = True
End Sub
Dim c As Integer
Public Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
setlabel(state.ToString)
Do While state = False
Debug.Print("BackgroundWorker1_DoWork ThreadID - " & Thread.CurrentThread.ManagedThreadId & " - state: " & state)
Thread.Sleep(100)
Loop
setlabel(state.ToString)
End Sub
Public Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Debug.Print("BackgroundWorker1_RunWorkerCompleted - State: " & state)
End Sub
Sub setlabel(text As String)
Label1.Invoke(Sub()
Label1.Text = text
End Sub)
End Sub
My problem is that:
Label's text doesn't change to False at the begining of
BackgroundWorker1_DoWork
in the Do While there is no Debug.Print
The output is:
Button1_Click ThreadID - 9
0
1
2
...
9998
9999
10000
BackgroundWorker1_RunWorkerCompleted - State: True
What do I wrong?
You are tying up the UI thread with the For loop:
For c = 0 To 10000
Debug.Print(c)
Next
You need to let the UI thread go idle for the call to setlabel(state.ToString)
to actually update the UI.
So you need to have something like a timer (or even a second background worker) wait a period of time before setting state = True.
Writing the title to try and explain my query I think was harder than the problem I'm actually facing :) - Anyway on to the question.
So I have a 20 second timer but I want two different things to happen on the first and second 10 seconds. Specifically to change the active tab.
So I thought to myself I'll just write an if Statement in the timer tick event that if it = 10 seconds to change to the second tab and when it hits 0 to switch back to the first, then to restart the timer.
Below is my code but nothing happens, I think the problem lies with reading the current remaining time.
Private timeLeft2 As Integer
Private Sub timerCountdown2()
timeLeft2 = 20
End Sub
Private Sub tabTimer_Tick(sender As Object, e As EventArgs) Handles tabTimer.Tick
If timeLeft2 = 10 Then
TabControlVertical1.SelectTab(1)
End If
If timeLeft2 = 0 Then
TabControlVertical1.SelectTab(0)
tabTimer.Stop()
tabTimer.Start()
End If
End Sub
The properties of my timer are enabled = true and Interval = 1000
What am I doing wrong?
You should set the timer to trigger the Tick event every 10 seconds, not every 20 (or 1 as by your edit above).
Every time the Tick event is triggered, you look at the value of a global boolean variable.
If this variable is true you execute the code reserved for the first 10 seconds and invert the value of the boolean. When the timer triggers again, you execute the code for the second case and invert again the value of the boolean
So, somewhere in your code or in the designer set the tabTimer interval to 10 seconds
tabTimer.Interval = 10000
and declare a global boolean variable (In the same forms class probably)
Private tabSwitcher as Boolean = True
Now the Tick event could be written as:
(no need to stop the timer if this process needs to continue)
Private Sub tabTimer_Tick(sender As Object, e As EventArgs) Handles tabTimer.Tick
If tabSwitcher = True Then
TabControlVertical1.SelectTab(1)
else
TabControlVertical1.SelectTab(0)
End If
tabSwitcher = Not tabSwitcher
End Sub
This is what I think you are asking:
do something in 10 timer ticks - timer set to 1000
do something else 10 timer ticks later
repeat
Try this
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Static ct As Integer = 0
ct += 1
If ct = 10 Then
'do 10 stuff here
Debug.WriteLine("10")
ElseIf ct = 20 Then
'do 20 stuff here
Debug.WriteLine("20")
'then reset ct <<<<<<<<<<<<
ct = 0
End If
End Sub
Friend Class timerCtrl : Inherits Timer
Private ReadOnly tickFunc As EventHandler = Nothing
Friend Sub New(ByRef theFunc As EventHandler, ByVal theInterval As Integer, Optional ByVal autoStart As Boolean = True)
tickFunc = theFunc
Interval = theInterval
AddHandler Tick, tickFunc
If (autoStart) Then Start()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
RemoveHandler Tick, tickFunc
End If
MyBase.Dispose(disposing)
End Sub
End Class
Friend Class TabClass
Private timerStep As Boolean = False
Private timerTabs As timerCtrl = Nothing
Friend Sub New()
timerTabs = New timerCtrl(AddressOf timerTabsTick, 10000)
End Sub
Private Sub timerTabsTick(ByVal sender As Object, ByVal e As EventArgs)
timerStep = Not timerStep
If timerStep Then
' condition 1
Else
' condition 2
End If
End Sub
End Class
a simple timer helper class for abstraction. to kill the timer, not just .Stop it, use timerTabs.Dispose(). eliminates the need to detach the event handler separately.
Seems to me that your timer is never getting to the value you are asking for in the if statements due to the fact that you have set the timer to the value of 20.
Also, I've use visual basics and am not to sure but doens't it need to be timeleft2.value?
Also, by stoping and starting the timer, it isn't actually restarting the timer, when you stop it say on 15 secs, and the restart, the timer restarts from 15 secs.
Try this.
If timeLeft2.Value = 10 Then
TabControlVertical1.SelectTab(1)
else if timeLeft2.Value = 0 Then
TabControlVertical1.SelectTab(0)
tabTimer.Stop()
timeLeft2.value = 0
tabTimer.Start()
End If
I have a form which, at present, is doing nothing but opening. The form has 2x controls - a 'Close' button and a Progress Bar. However, when I open the form, I get nothing. The Progress Bar just sits there doing nothing.
I've tried both Marquee (which I understand may not work in Windows 8) and Continuous, but I can't get anywhere with either.
This is how I'm showing the form when the program starts -
Sub main()
Dim ProgressForm As New formProgress
ProgressForm.ShowDialog()
End Sub
And below are the properties for the Progress Bar. Am I missing something that would get this bar working? Thanks.
Additional Information
For my full program, I did originally try using the Block style for the Progress Bar, but I kept getting the following error when trying to update the Progress Bar from a BackgroundWorker. This is why I am trying to get a simple Marquee/Continuous bar working instead.
Additional information: Cross-thread operation not valid: Control 'proWorking' accessed from a thread other than the thread it was created on.
if you use marquee style you have to set marqueeanimationspeed to some value
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(91, 118);
this.progressBar1.MarqueeAnimationSpeed = 50;
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(100, 23);
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.progressBar1.TabIndex = 0;
and use continuous style with marqueeanimationsspeed 0 to stop it
For the Progressbar to do something (apart from the Marquee Style) you need to set the Value property. If you have .Minimum=0 and .Maximum=100 then a .Value of 50 means that the Progressbar is half full. If you should use Continuous or Blocks Style depends on the Visual Styles settings and doesn't make a real difference here on Win 7 (maybe it does for example under Win XP).
The Marquee style means that you don't know how far your task has proceeded. The Progressbar then shows a continously moving piece (is only visible at runtime!!). I just tested it in Win 7 and it works.
Here's a little boilerplate I use with a BackgroundWorker and a ProgressBar and Label.
Public Class BackgroundWorkerUI
Private args As New ProgressArgs
Private Sub bw_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
'set ProgressBar style to Marquee
args.Style = ProgressBarStyle.Marquee
bw.ReportProgress(0) 'triggers the progress changed event to update UI on correct thread
'some long operation
Threading.Thread.Sleep(5000)
'Set ProgressBar style to Continuous
args.Style = ProgressBarStyle.Continuous
For i As Integer = 0 To 100
If bw.CancellationPending Then
e.Cancel = True
Exit For
End If
args.Current = i
args.Max = 100
args.Status = String.Format("({0} of {1}) Updating...", args.Current, args.Max)
bw.ReportProgress(0)
'some operation
Threading.Thread.Sleep(100)
Next
End Sub
Private Sub bw_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bw.ProgressChanged
lblStatus.Text = args.Status
If args.Style = ProgressBarStyle.Marquee Then
bar.Style = args.Style
bar.MarqueeAnimationSpeed = 15
Else
bar.Style = ProgressBarStyle.Continuous
bar.Minimum = args.Min
bar.Maximum = args.Max
bar.Value = args.Current
End If
End Sub
Private Sub bw_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
If e.Error IsNot Nothing Then
MessageBox.Show(e.Error.Message, "Background Worker Exception", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
If e.Cancelled Then
lblStatus.Text = "Operation canceled"
Else
lblStatus.Text = "Done"
End If
End If
End Sub
Private Sub BackgroundWorkerUI_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If bw.IsBusy Then
Dim r = MessageBox.Show("A background process is still running. Are you sure you want to quit?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If r = Windows.Forms.DialogResult.Yes Then
bw.CancelAsync()
End If
e.Cancel = True
End If
End Sub
Private Class ProgressArgs
Inherits EventArgs
Public Property Status As String
Public Property Current As Integer
Public Property Min As Integer
Public Property Max As Integer
Public Property Style As ProgressBarStyle
Public Sub New()
Status = ""
Current = 0
Min = 0
Max = 0
Style = ProgressBarStyle.Continuous
End Sub
End Class
End Class
My vb.net 4.0 project is in-house trading platform.
A separate form called MsgPad contains a Datagridview called MsgPadDG.
The Form-load sub does two things: formats the datagridview and defines a timer for the gridview refresh.
A second Sub, UpdateMyMsg, receives updates from a threaded UIUpdaterEngine by means of NewMessage events, and updates the MyMessages Object (fundamentally a datatable).
A third sub, UpdateMdgPadDGW, updates/refreshes the datagridview using the invoke method (probably not necessary, because the timer is defined in the UI thread).
Finally The Datagridview properties: rows are defined as not user-editable, AutoSizeRowsMode = Displayedcells and ScrollBars = Both.
My problem is that the entire App crashes when the updating process adds rows beyond the limit of the datagridview, causing scrollbars to appear.
I tried without the timer (invoking datasource update directly in the UpdateMyMsg Sub), with the timer, but the problem is always there.
Weird thing: The Try-Catch block doesn't catch anything.
Thanks in advance for your replays.
Edoardo
HERE COMES THE CODE:
Public Class MsgPad
Private WithEvents _MyUIUpdaterEngine As MyUIUpdaterEngine = MyUIUpdaterEngine.Instance
Private MyMessages As New Messages
Private LastUpdate As DateTime = TimeValue("08:00:00")
Private ObjLock As New Object
Private NewMessages As Boolean = False
Dim UpdateDGWTimer As New System.Timers.Timer
Private Sub MsgPad_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Try
'--
MsgPadDG.DataSource = MyMessages.DTable
'--
With MsgPadDG
'--
.Columns("Msg_ID").Visible = False
.Columns("I_CommandID").Visible = False
.Columns("B_ID").Visible = False
.Columns("F_ID").Visible = False
.Columns("T_TV").Visible = False
.Columns("T_Sign").Visible = False
.Columns("T_Mde").Visible = False
.Columns("T_QU").Visible = False
.Columns("T_QI").Visible = False
.Columns("T_QF").Visible = False
.Columns("T_PI").Visible = False
.Columns("T_PF").Visible = False
'--
.Columns("Msg_RecDate").HeaderText = "Date"
.Columns("Msg_RecDate").Width = 50
.Columns("Msg_RecDate").DefaultCellStyle.Format = "HH:mm:ss"
.Columns("I_Symbol").HeaderText = "Symbol"
.Columns("I_Symbol").Width = 80
.Columns("I_Des").HeaderText = "Des"
.Columns("I_Des").Width = 180
.Columns("Msg_Text").HeaderText = "Message"
.Columns("Msg_Text").Width = 250
'--
End With
'--Crea timer per l'Update del DAtagridView
AddHandler UpdateDGWTimer.Elapsed, AddressOf UpdateMsgPadDGW
UpdateDGWTimer.Interval = 3500
UpdateDGWTimer.Enabled = True
.....
.....
End Sub
Private Sub UpdateMyMsg(ByVal CRec As OrderRecord, ByVal Msg As String) Handles _MyUIUpdaterEngine.NewMessage
Try
SyncLock ObjLock
'--Aggiorna la tabella MyMessages
MyMessages.Add(CRec, Msg)
NewMessages = True
'--Parla messaggio
Dim synth As New SpeechSynthesizer
Dim TalkMsg As String = Msg
If CRec.I_Des <> Nothing Then TalkMsg += " su: " + CRec.I_Des
synth.SpeakAsync(TalkMsg)
End SyncLock
.....
.....
End Sub
Private Sub UpdateMsgPadDGW(source As Object, e As ElapsedEventArgs)
'--Aggiorna MesssagePad
Try
SyncLock ObjLock
If NewMessages Then
MsgPadDG.ControlInvoke(Sub(l)
MsgPadDG.DataSource = MyMessages.DTable
MsgPadDG.Refresh()
MsgPadDG.ClearSelection()
End Sub)
NewMessages = False
End If
End SyncLock
.....
.....
End Sub
'--Aggiorna la tabella MyMessages
MyMessages.Add(CRec, Msg)
NewMessages = True
That's a fatal bug, the DGV is bound to MyMessages. So calling Add() in a worker thread causes the control to be updated from the wrong thread. You normally get an IllegalOperationException from accessing a control from the wrong thread but that unfortunately doesn't work for bound data.
You'll need to do this differently, have the worker add messages to a List instead. Then in the invoked code update MyMessages with these new messages.
Also note that your Timer is not a safe timer like you assumed in your question. Only a System.Windows.Forms.Timer is a safe one whose Tick event handler will run on the UI thread. You then also don't need to invoke anymore.