Search code examples
vb.netwinformsbackgroundworker

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

Solution

  • 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...

    enter image description here