vb2012 textbox backcolor does't change using thread - vb.net

I wrote very simple thread example.
Make normal form1 and drop 1 textbox
run thread work on form load
thread change a textbox backcolor looks like switch on/off
But, it doesn't work.....
Can you tell me why doesn't work??
see my source.
=====================================================================
Imports System.Threading
Public Class Monitor
Public wObj As Worker = New Worker()
Public MyThread As Thread = New Thread(AddressOf wObj.DoWork)
Public Sub ChangeTBColor(pOption As Integer)
If pOption = 1 Then
tb1.BackColor = Color.Aqua
Else
tb1.BackColor = Color.Red
End If
End Sub
Private Sub Monitor_Load(sender As Object, e As EventArgs) Handles Me.Load
MyThread.Start()
Console.WriteLine("Running OrgThread..")
Console.WriteLine("Stop running")
End Sub
Private Sub BtnThreadStop_Click(sender As Object, e As EventArgs) Handles BtnThreadStop.Click
Me.wObj.RequestStop()
End Sub
End Class
Public Class Worker
Private LoopStop As Boolean = True
Public Sub DoWork()
Console.WriteLine("User Thread Start!")
Dim iTemp As Integer = 0
While (LoopStop)
Monitor.ChangeTBColor(iTemp Mod 2)
iTemp = iTemp + 1
Thread.Sleep(500)
End While
Console.WriteLine("User Thread End.!")
End Sub
Public Sub RequestStop()
LoopStop = False
End Sub
End Class

As already mentioned, your starting a new thread for your work, the issue is your trying to change the color for a control that need invoking. With this said, we need a delegate for when the control needs to be invoked... In my example, I used one class that handles this all and works great. Also please make sure to import System.ComponentModel because this is needed for the BackgroundWorker... I used the background worker as it takes all the heavy lifting off you would need...
Imports System.ComponentModel
Imports System.Threading
Public Class Monitor
Delegate Sub SetColor(ByVal clr As Integer) 'Your delegate..
Private WithEvents bw As New BackgroundWorker
Public Sub ChangeTBColor(pOption As Integer)
If Me.tb1.InvokeRequired Then 'Invoke if required...
Dim d As New SetColor(AddressOf ChangeTBColor) 'Your delegate...
Me.Invoke(d, New Object() {pOption})
Else
If pOption = 1 Then
tb1.BackColor = Color.Aqua
Else
tb1.BackColor = Color.Red
End If
End If
End Sub
Private Sub Monitor_Load(sender As Object, e As EventArgs) Handles Me.Load
bw.WorkerSupportsCancellation = True
Console.WriteLine("Running OrgThread..")
bw.RunWorkerAsync()
End Sub
Private Sub bw_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
Dim iTemp As Integer = 0
Dim LoopStop As Boolean = True
Console.WriteLine("User Thread Start!")
While (LoopStop)
If Not (bw.CancellationPending) Then
ChangeTBColor(iTemp Mod 2)
iTemp = iTemp + 1
Thread.Sleep(500)
Else
e.Cancel = True
LoopStop = False
End If
End While
End Sub
Private Sub bw_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
Console.WriteLine("User Thread End.!")
End Sub
Private Sub BtnThreadStop_Click(sender As Object, e As EventArgs) Handles BtnThreadStop.Click
If bw.IsBusy Then
bw.CancelAsync()
Else
Console.WriteLine("Running OrgThread..")
bw.RunWorkerAsync()
End If
End Sub
End Class
Here's my screenshot of my test... This is tried and tested. Please be sure to vote if this helps you!

Related

How can I cancel a thread?

I do something in a thread. But sometimes I don't want to wait till all pings are finished.
How can I cancel a thread?
Can you show me please the code?
Private Sub Start_Button_Click(sender As Object, e As EventArgs) Handles Start_Button.Click
DoSomething()
End Sub
Private Sub Cancel_Button_Click(sender As Object, e As EventArgs) Handles Cancel_Button.Click
THRD.Cancel '<-- Thread cancel!??!???
End Sub
Sub DoSomething()
Dim THRD As New Thread(Sub()
Dim IPArea As String = "192.168.1."
Dim LastIP As Integer
For LastIP = 0 To 255
Dim TestIP As String = IPArea & CStr(LastIP)
If My.Computer.Network.Ping(TestIP, 10) Then
ListBox1.Items.Add(TestIP)
End If
Next
End Sub)
THRD.IsBackground = True
THRD.Start()
End Sub
Here is my working solution and this solution is only to show how to move the THRD as a form level variable to allow stopping it when clicking the cancel button. I added some validations to prevent exceptions.
Public Class Form1
Private THRD As Threading.Thread
Private Sub Start_Button_Click(sender As Object, e As EventArgs) Handles Start_Button.Click
DoSomething()
End Sub
Private Sub Cancel_Button_Click(sender As Object, e As EventArgs) Handles Cancel_Button.Click
If THRD IsNot Nothing AndAlso THRD.IsAlive Then
THRD.Abort() '<-- Thread cancel!??!???
THRD = Nothing
AddToList("Stopped.")
Else
AddToList("Thread not running.")
End If
End Sub
Sub DoSomething()
If THRD IsNot Nothing AndAlso THRD.IsAlive Then
AddToList("Still working...")
Exit Sub
End If
THRD = New Threading.Thread(Sub()
Dim IPArea As String = "192.168.1."
Dim LastIP As Integer
For LastIP = 0 To 255
Dim TestIP As String = IPArea & CStr(LastIP)
If My.Computer.Network.Ping(TestIP, 10) Then
AddToList(TestIP)
End If
Next
AddToList("Done")
End Sub)
THRD.IsBackground = True
THRD.Start()
End Sub
''' <summary>
''' Thead-safe add value to list.
''' </summary>
''' <param name="value">The value.</param>
Private Sub AddToList(value As String)
If ListBox1.InvokeRequired Then
ListBox1.Invoke(Sub() ListBox1.Items.Add(value))
Else
ListBox1.Items.Add(value)
End If
End Sub
End Class

How to track multiple BackgroundworkerX.Runworkercompleted operations

I am trying to use a single handler to cover the end of multiple backgroundworker activities and cannot find a way to get the information about the specific backgroundworker using the backgroundworkercompleted event.
My code to catch the event is as below:
Private Sub BGx_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted, BackgroundWorker2.RunWorkerCompleted, BackgroundWorker3.RunWorkerCompleted, BackgroundWorker4.RunWorkerCompleted, BackgroundWorker5.RunWorkerCompleted, BackgroundWorker6.RunWorkerCompleted, BackgroundWorker7.RunWorkerCompleted, BackgroundWorker8.RunWorkerCompleted
'Do work here based on completed Backgroundworker
For BG = 1 To 8
If Not DSWorkers(BG).IsBusy Then
If DStatus(BG) = -2 Then : DStatus(BG) = -1 : End If
End If
Next
Complete()
End Sub
There is nothing on the "Do Work Here" section because I do not know how to capture and have been unable to find details of the backgroundworkercompleted event id.
Please - any pointers as to how I can identify the specific completed BackgroundWorker
As with all event handlers, the sender parameter is a reference to the object that raised the event, so you can access the actual BackgroundWorker that has completed its work via that. If you need some data other than that, you assign it to the e.Result property in the DoWork event handler and get it back from the e.Result property in the RunWorkerCompleted event handler. e.Result works for getting data out of the DoWork event handler much as e.Argument works for getting data in.
Check this out for some examples of using BackgroundWorker objects, including passing data out using e.Result. You might also like to checkout my own BackgroundMultiWorker class, which basically combines the functionality of multiple BackgroundWorker objects into a single BackgroundMultiWorker object. It identifies each task using a token.
EDIT:
Here's an example that may help with this issue and your task in general:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private ReadOnly resultsByWorker As New Dictionary(Of BackgroundWorker, BackgroundWorkerResult)
Private ReadOnly rng As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'The NumericUpDown is used to select the index of a BackgroundWorker to cancel.
With NumericUpDown1
.DecimalPlaces = 0
.Minimum = 0
.Maximum = 9
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Create 10 BackgroundWorkers and run them.
For i = 1 To 10
Dim worker As New BackgroundWorker
resultsByWorker.Add(worker, New BackgroundWorkerResult)
AddHandler worker.DoWork, AddressOf workers_DoWork
AddHandler worker.RunWorkerCompleted, AddressOf workers_RunWorkerCompleted
worker.WorkerSupportsCancellation = True
worker.RunWorkerAsync()
Next
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim index = Convert.ToInt32(NumericUpDown1.Value)
Dim worker = resultsByWorker.Keys.ToArray()(index)
If worker.IsBusy Then
'Cancel the BackgroundWorker at the specified index.
worker.CancelAsync()
End If
End Sub
Private Sub workers_DoWork(sender As Object, e As DoWorkEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
'Do work for a random number of seconds between 10 and 20.
Dim period = rng.Next(10, 20 + 1)
For i = 0 To period
If worker.CancellationPending Then
e.Cancel = True
Return
End If
'Simulate work.
Thread.Sleep(1000)
Next
'The work was completed without being cancelled.
e.Result = period
End Sub
Private Sub workers_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
Dim result = resultsByWorker(worker)
If e.Cancelled Then
result.WasCancelled = True
Else
result.Result = CInt(e.Result)
End If
Dim workers = resultsByWorker.Keys.ToArray()
If Not workers.Any(Function(bgw) bgw.IsBusy) Then
'All work has completed so display the results.
Dim results As New List(Of String)
For i = 0 To workers.GetUpperBound(0)
worker = workers(i)
result = resultsByWorker(worker)
results.Add($"Worker {i} {If(result.WasCancelled, "was cancelled", $"completed {result.Result} iterations")}.")
Next
MessageBox.Show(String.Join(Environment.NewLine, results))
End If
End Sub
End Class
Public Class BackgroundWorkerResult
Public Property WasCancelled As Boolean
Public Property Result As Integer
End Class
Here is that example reworked to use a single instance of the BackgroundMultiWorker is linked to instead of multiple instances of the BackgroundWorker class.
Imports System.Threading
Public Class Form1
Private WithEvents worker As New BackgroundMultiWorker With {.WorkerSupportsCancellation = True}
Private ReadOnly results(9) As BackgroundWorkerResult
Private ReadOnly rng As New Random
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'The NumericUpDown is used to select the index of a BackgroundWorker to cancel.
With NumericUpDown1
.DecimalPlaces = 0
.Minimum = 0
.Maximum = results.GetUpperBound(0)
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Create 10 BackgroundWorkers and run them.
For i = 0 To results.GetUpperBound(0)
results(i) = New BackgroundWorkerResult
worker.RunWorkerAsync(i)
Next
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim index = Convert.ToInt32(NumericUpDown1.Value)
If worker.IsBusy(index) Then
'Cancel the BackgroundWorker at the specified index.
worker.CancelAsync(index)
End If
End Sub
Private Sub worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles worker.DoWork
'Do work for a random number of seconds between 10 and 20.
Dim period = rng.Next(10, 20 + 1)
For i = 0 To period
If worker.IsCancellationPending(e.Token) Then
e.Cancel = True
Return
End If
'Simulate work.
Thread.Sleep(1000)
Next
'The work was completed without being cancelled.
e.Result = period
End Sub
Private Sub workers_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
Dim result = results(CInt(e.Token))
If e.Cancelled Then
result.WasCancelled = True
Else
result.Result = CInt(e.Result)
End If
If Not worker.IsBusy() Then
'All work has completed so display the results.
Dim output As New List(Of String)
For i = 0 To results.GetUpperBound(0)
result = results(i)
output.Add($"Task {i} {If(result.WasCancelled, "was cancelled", $"completed {result.Result} iterations")}.")
Next
MessageBox.Show(String.Join(Environment.NewLine, output))
End If
End Sub
End Class
Public Class BackgroundWorkerResult
Public Property WasCancelled As Boolean
Public Property Result As Integer
End Class

How to access richtextbox via different thread

Hey guys my question is how can I access (update/read) richtextbox in a thread.
I just created a very simple code for you to understand what I am doing. I searched some articles on internet mentioned about invoke, delegate or backgroundworker, hope someone can come and tell me which and how to use. Really thanks.
Imports System.Threading
Public Class form1
Dim flag As Boolean = True
Dim startbtn As Thread
Dim stopbtn As Thread
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
startbtn = New Thread(AddressOf startfuction)
startbtn.Start()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
stopbtn = New Thread(AddressOf stopfunction)
stopbtn.Start()
End Sub
'************** thread 1
Private Sub startfuction()
flag = True
While flag = True
richtextbox1.text = "Your process started" 'error
End While
End Sub
'************** thread 2
Private Sub stopfunction()
flag = False
startbtn.Abort()
MsgBox("You ended the process")
End Sub
End Class
Imports System.Threading
Public Class Form1
Dim flag As Boolean = True
Dim startbtn As Thread
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
startbtn = New Thread(AddressOf startfuction)
startbtn.Start()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
flag = False
MsgBox("You ended the process")
End Sub
'************** thread 1
Private Sub startfuction()
flag = True
While flag = True
Me.Invoke(Sub() RichTextBox1.Text = "Your process started") 'error
End While
Me.Invoke(Sub() RichTextBox1.Text = "Your process stopped") 'error
End Sub
End Class
EDIT 1
Also when running threads, When you goto close your app you could run into problems because your threads will end prematurely..
do something like
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
flag = False
Application.DoEvents()
End Sub

How to get threading to work?

Here is the code:
Imports System.Threading
Partial Public Class frmTermsOfUse
Inherits Form
Public Sub New()
InitializeComponent()
rtxtContent.Text = My.Resources.TermsOfUse
End Sub
Private Shared [exit] As Boolean = True
Private Sub btnAccept_Click(sender As Object, e As EventArgs)
XMLSettings.WriteValue("ShowToU", (Not chkDontShowAgain.Checked).ToString())
[exit] = False
Me.Close()
End Sub
Private Sub frmTermsOfUse_Load(sender As Object, e As EventArgs)
lblToU.Left = (Me.Width \ 2) - (lblToU.Width / 2)
Dim t As New Thread(AddressOf Wait20Sec)
t.Start()
End Sub
Private Sub Wait20Sec()
For i As Integer = 19 To 0 Step -1
System.Threading.Thread.Sleep(1000)
Try
Me.Invoke(DirectCast(Sub() btnAccept.Text = "Accept (" & i & ")", MethodInvoker))
Catch
End Try
Next
Me.Invoke(DirectCast(Sub()
btnAccept.Text = "Accept"
btnAccept.Enabled = True
End Sub, MethodInvoker))
End Sub
Private Sub btnDecline_Click(sender As Object, e As EventArgs)
System.Diagnostics.Process.GetCurrentProcess().Kill()
End Sub
Private Sub frmTermsOfUse_FormClosing(sender As Object, e As FormClosingEventArgs)
If e.CloseReason = CloseReason.UserClosing AndAlso [exit] Then
System.Diagnostics.Process.GetCurrentProcess().Kill()
End If
End Sub
End Class
I don't get the countdown on the button to work.
Button is disabled when I launch the application and does nothing else.
How can I fix this?
Already thanks for the help

VB Net - Pass data from Thread to Main GUI

I've managed to get the data I want in a thread, however I'm having trouble getting my head around passing the data back into my main thread (GUI).
I'm grabbing data from a network stream in a thread, then need to pass it to my main thread in order to pass to other classes etc.
I've seen it mentioned to use backgroundworker for this, but as I'm expecting it to gather data periodically and never stop, I thought a separate thread for this would be best, but I'm very new to multithreading.
If threading is the correct way to go, how can I pass data from it back to my main thread in order to use this for other stuff? I've seen delegates and events mentioned a lot but can't see how I'd pass data with these?
Thanks
Please study this example, and let me know if it fits your requirements:
Controls required: lstItems (ListBox), btnStart (Button), btnStop (Button), Timer1 (Timer).
Form1 code:
Public Class Form1
Dim p_oStringProducer As StringProducer
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
p_oStringProducer = New StringProducer
p_oStringProducer.Start()
Timer1.Enabled = True
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) _
Handles btnStop.Click
Timer1.Enabled = False
p_oStringProducer.Stop()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim asQueue As Concurrent.ConcurrentQueue(Of String) =
p_oStringProducer.MessageQueue
While asQueue.Count > 0
Dim sItem As String = Nothing
asQueue.TryDequeue(sItem)
lstItems.Items.Add(sItem)
End While
End Sub
End Class
StringProducer code:
Imports System.Threading.Tasks
Public Class StringProducer
Private p_fKeepRunning As Boolean
Private p_oTask As task
Private p_aMessageQueue As Concurrent.ConcurrentQueue(Of String)
Private p_iNextMessageId As Integer
Public ReadOnly Property MessageQueue As _
Concurrent.ConcurrentQueue(Of String)
Get
Return p_aMessageQueue
End Get
End Property
Sub New()
p_oTask = New Task(AddressOf TaskBody)
p_aMessageQueue = New Concurrent.ConcurrentQueue(Of String)
p_iNextMessageId = 0
End Sub
Public Sub Start()
p_fKeepRunning = True
p_oTask.Start()
End Sub
Public Sub [Stop]()
p_fKeepRunning = False
End Sub
Private Sub TaskBody()
While p_fKeepRunning
Threading.Thread.Sleep(2000)
p_aMessageQueue.Enqueue("Message #" & p_iNextMessageId)
p_iNextMessageId += 1
End While
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
Me.Stop()
End Sub
End Class
This was not extensively tested, but it should give you a head start.