Results 1 to 8 of 8

Thread: populate ListView using a thread

  1. #1

    Thread Starter
    New Member remusrigo's Avatar
    Join Date
    Aug 2011
    Location
    Romania, Timisoara
    Posts
    2

    populate ListView using a thread

    Hi all

    I have a procedure that populates a ListView, but it does a lot of work and it takes too much time to display all it's items (about 100-200 items). I want to use a thread to display the items as it's extracting data (real-time).

    I posted a question earlier here, but using a ListView it's not working for me...

    Code:
    Imports System.Threading
    Imports System.ComponentModel
    
    Public Class Form1
       Dim li As ListViewItem = lv.Items.Add("")
    
       Dim myThread As System.Threading.Thread = New Thread(AddressOf Me.AddItems)
    
       Public Delegate Sub ItemAdder(ByVal Input As String)
       Dim objItemAdder As ItemAdder
    
       Public Sub AddItemsDelgate(ByVal ItemToAdd As String)
          li = lv.Items.Add("")
          li.Text = ItemToAdd
       End Sub
    
       Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
          objItemAdder = New ItemAdder(AddressOf AddItemsDelgate)
          myThread.Start()
       End Sub
    
       Public Sub AddItems()
          Dim i As Int32
          Dim n As Double = 1
          Dim s As String = ""
    
          For i = 0 To 100
             s = n.ToString
             Me.Invoke(objItemAdder, New Object() {s})
             n *= 2
             Thread.Sleep(100)
          Next
    
       End Sub
    
    End Class
    Last edited by remusrigo; Jan 22nd, 2012 at 11:15 AM.

  2. #2
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    26,413

    Re: populate ListView using a thread

    try this. you need a listview + a backgroundworker:

    vb Code:
    1. Public Class Form1
    2.  
    3.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    4.         ListView1.Columns.Add("Column1")
    5.         ListView1.View = View.Details
    6.         BackgroundWorker1.WorkerReportsProgress = True
    7.         BackgroundWorker1.RunWorkerAsync()
    8.     End Sub
    9.  
    10.     Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    11.         Dim n As Double = 1
    12.         Dim s As String = ""
    13.  
    14.         For i As Integer = 0 To 100
    15.             s = n.ToString
    16.             Dim item As New ListViewItem(s)
    17.             n *= 2
    18.             Threading.Thread.Sleep(100)
    19.             BackgroundWorker1.ReportProgress(0, item)
    20.         Next
    21.  
    22.     End Sub
    23.  
    24.     Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    25.         ListView1.Items.Add(DirectCast(e.UserState, ListViewItem))
    26.         ListView1.EnsureVisible(ListView1.Items.Count - 1)
    27.     End Sub
    28.  
    29. End Class

  3. #3
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: populate ListView using a thread

    I assume that what you're showing is a simplification of a real world scenario. I hope so because the only reason that you'd consider using a secondary thread is if getting the data itself is slow. Otherwise, you're actually going to slow down the population of the ListView rather than speed it up.

    Even if getting the data is slow, you'd still want there to be a reasonable gap between the data being available for one item and the next. If not then you would not use the ProgressChanged event as .paul. has suggested to add the items one by one because that will still be slow. Rather, you'd get the all the data and create all the items first, storing them in an array, then use the RunWorkerCompleted event to add them all in a batch using AddRange.

    You might like to follow the CodeBank link in my signature and check out my thread on Using The BackgroundWorker for code examples contrasting the two options. The examples use a ListBox rather than a ListView but the principle is exactly the same.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  4. #4
    Old Member moeur's Avatar
    Join Date
    Nov 2004
    Location
    Wait'n for Free Stuff
    Posts
    2,712

    Re: populate ListView using a thread

    The reason to make the loading of listview items asynchronous is to prevent the UI from locking up.
    This is how I would do it.
    Code:
        
    Public Class Form1
    
        Public Delegate Sub AddItemsDelgate()
        Public Sub AddItems()
            Dim i As Int32
            Dim n As Double = 1
            Dim s As String = ""
            For i = 0 To 100
                s = n.ToString
                LV.Items.Add(s)
                n *= 2
            Next
        End Sub
    
        Private Sub LV_HandleCreated(sender As Object, e As System.EventArgs) Handles LV.HandleCreated
            LV.BeginInvoke(New AddItemsDelgate(AddressOf AddItems))
        End Sub
    
    End Class
    Notice that I start the routine from the listview's HandleCreated Event.
    This is because it will fail if the Listview does not yet have a handle as would be the case from the form1.Load event.

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: populate ListView using a thread

    Quote Originally Posted by moeur View Post
    The reason to make the loading of listview items asynchronous is to prevent the UI from locking up.
    The items themselves MUST be added to the ListView on the UI thread so what point is there to creating them on a secondary thread and then delegating back to the UI thread several times per second to add them one by one? Exactly how responsive is the UI going to be under those circumstances? Plus, as I said, you're actually going to slow donw the whole process by adding all the context switching and redrawing the ListView after every item.
    Quote Originally Posted by moeur View Post
    Notice that I start the routine from the listview's HandleCreated Event.
    This is because it will fail if the Listview does not yet have a handle as would be the case from the form1.Load event.
    Um, no. If you add items to the ListView in the designer then the code generated to actually create and add them at run time is in the InitializeComponent method. That is executed well before the Load event handler, so how could adding them in the Load event handler possibly be an issue?

    Also, your code makes no sense. You are handling an event on the UI thread and then calling BeginInvoke on the ListView, which will delegate to the thread that owns the ListView, i.e. the UI thread. You aren't using multiple threads at all so the invocation is pointless.

    There are only three reasonable ways to approach this:

    1. If getting the data, creating the items and adding them to the ListView doesn't take a long time when the form is not visible then do it all on the UI thread in the Load event handler.

    2. If the data is being retrieved in a batch and that process is slow, retrieve the data and create the items on a secondary thread, then add all the items to the ListView in a batch on the UI thread. The simplest way to do that is using a BackgroundWorker. Get the data and create the items in the DoWork event handler and populate the ListView in the RunWorkerCompleted event handler.

    3. If the data is being retrieved piecemeal with a reasonable time period between pairs then retrieve the data and create the items on a secondary thread and add the items one by one on the UI thread. The simplest way to do that is using a BackgroundWorker. Get the data and create the items in the DoWork event handler and populate the ListView in the ProgressChanged event handler.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  6. #6
    Old Member moeur's Avatar
    Join Date
    Nov 2004
    Location
    Wait'n for Free Stuff
    Posts
    2,712

    Re: populate ListView using a thread

    The nice thing about BeginInvoke is that it performs the action asynchronously (so it must run on a separate thread) So, if you want your form to load right away and have it be responsive while the items load, then this is the way to go. I'm not looking for an increase in speed, just more responsiveness.

    If, on the otherhand, you want your form to remain invisible while it fills the listview, then do it in the load event without any additional threads.

    Um, no. If you add items to the ListView in the designer then the code generated to actually create and add them at run time is in the InitializeComponent method. That is executed well before the Load event handler, so how could adding them in the Load event handler possibly be an issue?
    You can load items into a listview in the form's load event, but you cannot run BeginInvoke on a control until it has been assigned a handle. This happens after the form's Load event.

  7. #7
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    26,413

    Re: populate ListView using a thread

    i thought the whole idea here was to add the items 1 by 1 in an adding/scrolling effect instead of adding all the items in 1 go?

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: populate ListView using a thread

    Quote Originally Posted by moeur View Post
    The nice thing about BeginInvoke is that it performs the action asynchronously (so it must run on a separate thread)
    Here's the definition of the BeginInvoke method from the MSDN documentation:
    Executes a delegate asynchronously on the thread that the control's underlying handle was created on.
    That makes sense because it's the whole point of an invocation: to execute a method on the thread that owns the control. If your HandleCreated event handler is executed on the UI thread (which I assume that it is, although I haven't tested it to make sure) then how can BeginInvoke execute a method on a different thread if it is delegating to the thread that owns the ListView, which is the UI thread?
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width