Prevent "Not Responding" - vb.net

When running code on a Form, sometimes (usually when looping) the program title displays "Not Responding" even though its still running fine.
In my case, I'm looping and filling an array > array to DataTable > DataTable to DataGridView.
example:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim ArrayNew(1000001, 2) As Object
For i = 0 To 1000000
For j = 0 To 1
ArrayNew(i, j) = "TEST"
Next j
Next i
Table1 = New DataTable("EXAMPLETABLE")
Table1.Columns.Add("COLUMN1")
Table1.Columns.Add("COLUMN2")
Table1.Columns.Add("COLUMN3")
Dim RowNo As Integer = 0
Do
Table1.Rows.Add(New String() {RowNo + 1, ArrayNew(RowNo, 0), ArrayNew(RowNo, 1)})
RowNo = RowNo + 1
Loop Until ArrayNew(RowNo, 1) = ""
DataGridView1.DataSource = Table1
End Sub
I read somewhere that I should be using threads? or BackgroundWorker but I'm unsure how to use these here like a lot of questions point to BackgroundWorker Class.
But I don't understand C#.

The first thing to do is to drop a BackgroundWorker component onto the form. Click on the component and setup handlers for the DoWork, RunWorkerCompleted, and ProgressChanged events. Then, assuming you use all the default names, your code will look like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
backgroundWorker1.RunWorkerAsync()
End Sub
Private Sub backgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles backgroundWorker1.DoWork
Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
Dim Table1 As New DataTable("EXAMPLETABLE")
Table1.Columns.Add("COLUMN1")
Table1.Columns.Add("COLUMN2")
Table1.Columns.Add("COLUMN3")
Dim Size As Integer = 1000000
For i As Integer = 1 To Size
Table1.Rows.Add(New String() {i.ToString(), "Test", "Test"})
If i Mod 50 = 0 Then
worker.ReportProgress(i * 100.0 / Size) 'Report as a percentage of the total
End If
Next
e.Result = Table1
End Sub
Private Sub backgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
DataGridView1.DataSource = DirectCast(e.Result, DataTable)
End Sub
Private Sub backgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles backgroundWorker1.ProgressChanged
'Check e.ProgressPercentage here
End Sub
This is the basic example. You can also use this to do things like report progress and make sure it's not already busy before starting it. More information (including samples in C#, but translates to VB.Net easily) is here:
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netcore-3.1

Related

the codes shows a notif 'Index was out of range. Must be non-negative and less than the size of the collection

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Cursor = Cursors.AppStarting
Dim id As Integer
Dim fx As frmItemEntry
id = DataGridView1.SelectedRows(0).Cells("id").Value
fx = New frmItemEntry(id)
Button4.PerformClick()
fx.ShowDialog()
Cursor = Cursors.Default
End Sub
try this code from a blog but, am don't know where is wrong
Obviously no rows are selected. Therefore you cannot access row 0. The best way to deal with this situation is to disable the button when no row is selected.
To do this you must handle the SelectionChanged event
Private Sub DataGridView1_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs) _
Handles DataGridView1.SelectionChanged
Button2.Enabled = DataGridView1.SelectedRows.Count > 0
End Sub
Also, disable the button by default in the Form designer. This code will enable it whenever a selection is made.
Another possibility is to do something like this:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim id As Integer
Dim fx As frmItemEntry
If DataGridView1.SelectedRows.Count > 0 Then
Cursor = Cursors.AppStarting
id = DataGridView1.SelectedRows(0).Cells("id").Value
fx = New frmItemEntry(id)
Button4.PerformClick()
fx.ShowDialog()
Cursor = Cursors.Default
Else
MessageBox.Show("Please select a row")
End If
End Sub

Adding Conditions to the Listboxes from .csv files

enter image description herei am looking for this problem for very long time and I have found nothing about that... :( .
So, I have 2 .csv files. In the .csv file 1 are the listboxes and all of these listboxes have one number between 1 and 6. In the .csv file 2 are some tools like (audi, bmw) and the tools have one numer between 1 and 6 too.
Example:
.csv file 1
Listbox 1 (X,Y,Length,Width) category 1
Listbox 2 (X,Y,Length,Width) category 2
Listbox 3 (X,Y,Length,Width) category 3
.csv file 2
Mercedes category 1
BMW category 2
Audi category 3
So, I want compare the 2 .csv files and allow the listbox 2 for only BMW to drag and drop.
If I drop categroy 2 to category 1 in the listbox, it should be false.
Imports System.Windows.Forms
Imports System.IO
Public Class Form2
Private meineListBoxen As New List(Of ListBox)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim strZeilen() As String
Dim strFelder() As String
Dim strZeilen0() As String
Dim strZeilen2() As String = IO.File.ReadAllLines("K:\Ruebel_Andreas\Modellpflege\Datenerfassung.csv")
Dim strFelder1() As String
strZeilen = File.ReadAllLines("K:\Ruebel_Andreas\Modellpflege\listboxpflege.csv")
strZeilen0 = IO.File.ReadAllLines("K:\Ruebel_Andreas\Modellpflege\Spritzguss.csv")
For i As Integer = 1 To strZeilen.GetUpperBound(0)
strFelder = strZeilen(i).Split(";")
meineListBoxen.Add(New ListBox)
With meineListBoxen(i - 1)
.Left = strFelder(0)
.Top = strFelder(1)
.Width = strFelder(2)
.Height = strFelder(3)
End With
Me.Controls.Add(meineListBoxen(i - 1))
Me.meineListBoxen(i - 1).AllowDrop = True
AddHandler meineListBoxen(i - 1).MouseDown, AddressOf meineListbox_MouseDown
AddHandler meineListBoxen(i - 1).DragDrop, AddressOf meineListbox_DragDrop
AddHandler meineListBoxen(i - 1).DragEnter, AddressOf meineListbox_DragEnter
Next
For a As Integer = 0 To strZeilen0.GetUpperBound(0)
Me.meineListBoxen(1 - 1).Items.Add(strZeilen0(a).Substring(0, strZeilen0(a).IndexOf(";")))
Next
For j As Integer = 1 To strZeilen2.GetUpperBound(0)
strFelder1 = strZeilen2(j).Split(" ")
With meineListBoxen(j)
.Items.Add(strFelder1(4))
End With
For Each itm In meineListBoxen(j).Items
If meineListBoxen(1 - 1).Items.Contains(itm) Then meineListBoxen(1 - 1).Items.Remove(itm)
Next
Next
End Sub
Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
Me.lblMaus.Text = "X: " & e.X & " , Y:" & e.Y
End Sub
Private source As ListBox
Private sourceIndex As Integer
Private Sub meineListbox_MouseDown(ByVal sendre As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs)
Dim aPoint As Point
Dim lbx As ListBox
Dim aIndex As Integer
lbx = CType(sendre, ListBox)
aPoint = New Point(e.X, e.Y)
aIndex = lbx.IndexFromPoint(aPoint)
Try
If aIndex <= 0 Then
source = lbx
sourceIndex = aIndex
lbx.DoDragDrop(lbx.Items(aIndex), DragDropEffects.All)
End If
Catch ex As Exception
MessageBox.Show("Bitte wählen Sie ein Werkzeug aus")
End Try
End Sub
Private Sub meineListbox_DragDrop(ByVal sender As System.Object,
ByVal e As System.Windows.Forms.DragEventArgs)
Dim lbx As ListBox
lbx = CType(sender, ListBox)
If Not source Is Nothing Then
source.Items.RemoveAt(sourceIndex)
End If
lbx.Items.Add(e.Data.GetData(DataFormats.Text))
End Sub
Private Sub meineListbox_DragEnter(ByVal sender As System.Object,
ByVal e As System.Windows.Forms.DragEventArgs)
If (e.Data.GetDataPresent(DataFormats.Text)) Then
e.Effect = DragDropEffects.All
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub cmdSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Speichern.Click
Dim w As New IO.StreamWriter("K:\Ruebel_Andreas\Modellpflege\Datenerfassung.csv")
For i = 0 To meineListBoxen.Count - 1
w.WriteLine(meineListBoxen.Item(i))
Next
w.Close()
End Sub
Private Sub WerkzeugHinzufügen_Click(sender As Object, e As EventArgs) Handles WerkzeugHinzufügen.Click
Process.Start("K:\Ruebel_Andreas\Modellpflege\Spritzguss.csv")
End Sub
Private Sub StellplatzHinzufügen_Click(sender As Object, e As EventArgs) Handles StellplatzHinzufügen.Click
Process.Start("K:\Ruebel_Andreas\Modellpflege\listboxpflege.csv")
End Sub
End Class
Private Sub cmdSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Speichern.Click
Using w As New StreamWriter("K:\Ruebel_Andreas\Modellpflege\Datenerfassung.csv")
For i = 0 To meineListBoxen.Count - 1
w.WriteLine(meineListBoxen.Item(i))
Next
End Using
End Sub
I find it odd that the Handles clause does not match the name of the method. The default would be Private Sub Speichern_Click
StreamWriter needs to be disposed. Using...End Using blocks will handle this even if there is an error.
Let's say you have 3 list boxes. Your loop will go from index 0 to index 2. WriteLine will call ToString on each item in the list of ListBox. I don't believe that a ListBox provides an implementation of ToString so you will get the fully qualified name of the object's type. I don't think you want this.
Private Sub cmdSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Speichern.Click
Using w As New StreamWriter("K:\Ruebel_Andreas\Modellpflege\Datenerfassung.csv")
For Each LB As ListBox In meineListBoxen
For Each item As String In LB.Items
w.WriteLine(item)
Next
Next
End Using
End Sub
You can initialize your variables as you declare them and with Option Infer they will be strongly typed (only works for local variables.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim strZeilen2 = File.ReadAllLines("K:\Ruebel_Andreas\Modellpflege\Datenerfassung.csv")
Dim strZeilen = File.ReadAllLines("K:\Ruebel_Andreas\Modellpflege\listboxpflege.csv")
Dim strZeilen0 = File.ReadAllLines("K:\Ruebel_Andreas\Modellpflege\Spritzguss.csv")
For i = 1 To strZeilen.GetUpperBound(0) 'Why are skipping the first line?
Dim strFelder = strZeilen(i).Split(";"c)
Dim LB As New ListBox
With LB
.Left = CInt(strFelder(0))
.Top = CInt(strFelder(1))
.Width = CInt(strFelder(2))
.Height = CInt(strFelder(3))
.AllowDrop = True
.Name = "ListBox" & i 'In case you need to refer to the control be name
End With
If i = 1 Then
For a As Integer = 0 To strZeilen0.GetUpperBound(0)
LB.Items.Add(strZeilen0(a).Substring(0, strZeilen0(a).IndexOf(";")))
Next
End If
AddHandler LB.MouseDown, AddressOf meineListbox_MouseDown
AddHandler LB.DragDrop, AddressOf meineListbox_DragDrop
AddHandler LB.DragEnter, AddressOf meineListbox_DragEnter
'I fleshed out the control before adding to the collections
Controls.Add(LB)
meineListBoxen.Add(LB)
Next
For j = 1 To strZeilen2.GetUpperBound(0) 'Again skipping first line, perhaps it is a title line
Dim strFelder1 = strZeilen2(j).Split(" "c)
meineListBoxen(j).Items.Add(strFelder1(4)) 'You are skipping the first list box
For Each itm In meineListBoxen(j).Items
If meineListBoxen(0).Items.Contains(itm) Then
meineListBoxen(0).Items.Remove(itm)
End If
Next
Next
End Sub
No need to define a point, just pass the coordinates to the IndexFromPoint method. You can use DirectCast to get the ListBox since we are sure the sender is a ListBox. DirectCast skips some of the checks that CType does so the method so is a bit faster. I am not sure if the check on the value of aIndex is necessary. Exception handling is heavy and shouldn't be used for a simple value check.
Private Sub meineListbox_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs)
Dim lbx = DirectCast(sender, ListBox)
Dim aIndex = lbx.IndexFromPoint(e.X, e.Y)
If aIndex < 0 Then
MessageBox.Show("Bitte wählen Sie ein Werkzeug aus")
Else
source = lbx
sourceIndex = aIndex
lbx.DoDragDrop(lbx.Items(aIndex), DragDropEffects.All)
End If
End Sub
Use IsNot when checking for null objects.
Private Sub meineListbox_DragDrop(sender As System.Object, e As System.Windows.Forms.DragEventArgs)
Dim lbx = DirectCast(sender, ListBox)
If source IsNot Nothing Then
source.Items.RemoveAt(sourceIndex)
End If
lbx.Items.Add(e.Data.GetData(DataFormats.Text))
End Sub
Updating this label over and over will just slow things down. Delete this Sub.
'Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
'lblMaus.Text = "X: " & e.X & " , Y: " & e.Y
'End Sub
I can't help more without correct data from your files. At one point, you are splitting by a semicolon and there aren't any in your sample data! The file content you showed is certainly not a CSV file.

How I can change the text of a single label from a string array vb.net?

so I want to display a string array in a single label that change the contents with a period of time ,
I ve tried every thing the timer, the background worker every thing , the problem when I use a loop inset a timer the interval in the start should be so long if the array items was so many so I tried the background worker but it not works
this is the code :
Dim array() As String = {"so", "nb", "de", "rty", "dcds"}
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
Dim delay As Integer = 2000
Dim interval As Integer = 100
Dim elapsed As Integer = 0
Dim pos As Integer = array.Length
While Not worker.CancellationPending
If (elapsed >= delay) Then
worker.ReportProgress(pos)
' change label text in the Progress event handler
pos = (pos + 1)
elapsed = 0
If (pos = array.Length) Then
Exit While
End If
End If
Thread.Sleep(interval)
End While
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim j As Integer
For j = 0 To array.Length
Label1.Text = array(j)
Next
End Sub
Your for loop is overwriting the .Text property of the lable on each iteration. You will also get an Index Out of Range exception because the indexes of an array are zero based. The highest index will be 1 less than the .Length.
Private arStr() As String = {"so", "nb", "de", "rty", "dcds"}
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim LabelText = String.Join(Environment.NewLine, arStr)
Label1.Text = LabelText
End Sub
EDIT
Private arStr() As String = {"so", "nb", "de", "rty", "dcds"}
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Static index As Integer
If index < arStr.Length Then
Label1.Text &= arStr(index) & Environment.NewLine
index += 1
End If
End Sub
With asynchronous approach you can display array values one after another without BackgroundWorker and explicitly created Timer.
Private _values As New String() From {"so", "nb", "de", "rty", "dcds"}
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each value In _values
await Task.Delay(2000)
Next
End Sub
With asynchronous approach you can prevent button click before all values are displayed in the simple way as you would do in synchronous code.
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim button = DirectCast(sender, Button);
button.Enabled = False
For Each value In _values
await Task.Delay(2000)
Next
button.Enabled = True
End Sub

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

Adding 150,000 records to a listview without freezing UI

I have a listview loop that is adding 150,000 items to my listview. For testing purposes I moved this code to a background worker with delegates, but it still freezes up the UI. I am trying to find a solution so that it can add these items in the background while I do other stuff in the app. What solutions do you guys recommend?
this is what I am using
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
ListView1.Clear()
ListView1.BeginUpdate()
bw.WorkerReportsProgress = True
bw.RunWorkerAsync()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If bw.IsBusy Then bw.CancelAsync()
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
For x = 1 To 125000
Dim lvi As New ListViewItem("Item " & x)
If bw.CancellationPending Then
e.Cancel = True
Exit For
Else
bw.ReportProgress(0, lvi)
End If
Next
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bw.ProgressChanged
Try
Dim lvi As ListViewItem = DirectCast(e.UserState, ListViewItem)
Me.ListView1.Items.Add(lvi)
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
ListView1.EndUpdate()
If Not e.Cancelled Then
Debug.Print("Done")
Else
Debug.Print("Cancelled")
End If
End Sub
End Class
Give this a try, it's a great example for what you would need... I also had a progress bar that shows the progress and such, see example image that is attached. Also I wasn't seeing any delegate that you need to perform such operation, mine has one that will be required. The reason is you are adding items to a control on the UI thread, in order to add items we need to know if an Invoke is required, if so we invoke otherwise we add the item to the control... Also I made the thread sleep, so it can take a break; this also prevents the UI from wanting to lock up here and there, now it's responsive with NO FREEZING.
Option Strict On
Option Explicit On
Public Class Form1
Delegate Sub SetListItem(ByVal lstItem As ListViewItem) 'Your delegate..
'Start the process...
Private Sub btnStartProcess_Click(sender As Object, e As EventArgs) Handles btnStartProcess.Click
lvItems.Clear()
bwList.RunWorkerAsync()
End Sub
Private Sub AddListItem(ByVal lstItem As ListViewItem)
If Me.lvItems.InvokeRequired Then 'Invoke if required...
Dim d As New SetListItem(AddressOf AddListItem) 'Your delegate...
Me.Invoke(d, New Object() {lstItem})
Else 'Otherwise, no invoke required...
Me.lvItems.Items.Add(lstItem)
End If
End Sub
Private Sub bwList_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bwList.DoWork
Dim intCount As Integer = CInt(txtCount.Text)
Dim dblPercent As Integer = 100
Dim intComplete As Integer = 0
Dim li As ListViewItem = Nothing
For i As Integer = 1 To CInt(txtCount.Text)
If Not (bwList.CancellationPending) Then
li = New ListViewItem
li.Text = "Item " & i.ToString
AddListItem(li)
Threading.Thread.Sleep(1) 'Give the thread a very..very short break...
ElseIf (bwList.CancellationPending) Then
e.Cancel = True
Exit For
End If
intComplete = CInt(CSng(i) / CSng(intCount) * 100)
If intComplete < dblPercent Then
bwList.ReportProgress(intComplete)
End If
If li IsNot Nothing Then
li = Nothing
End If
Next
End Sub
Private Sub bwList_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bwList.ProgressChanged
pbList.Value = e.ProgressPercentage
End Sub
Private Sub bwList_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bwList.RunWorkerCompleted
If pbList.Value < 100 Then pbList.Value = 100
MessageBox.Show(lvItems.Items.Count.ToString & " items were added!")
End Sub
Private Sub btnStopWork_Click(sender As Object, e As EventArgs) Handles btnStopWork.Click
bwList.CancelAsync()
End Sub
Private Sub btnRestart_Click(sender As Object, e As EventArgs) Handles btnRestart.Click
pbList.Value = 0
lvItems.Items.Clear()
txtCount.Text = String.Empty
End Sub
End Class
Screenshot in action...