|
-
Jan 22nd, 2012, 11:08 AM
#1
Thread Starter
New Member
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.
-
Jan 22nd, 2012, 12:41 PM
#2
Re: populate ListView using a thread
try this. you need a listview + a backgroundworker:
vb Code:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ListView1.Columns.Add("Column1")
ListView1.View = View.Details
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim n As Double = 1
Dim s As String = ""
For i As Integer = 0 To 100
s = n.ToString
Dim item As New ListViewItem(s)
n *= 2
Threading.Thread.Sleep(100)
BackgroundWorker1.ReportProgress(0, item)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ListView1.Items.Add(DirectCast(e.UserState, ListViewItem))
ListView1.EnsureVisible(ListView1.Items.Count - 1)
End Sub
End Class
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jan 23rd, 2012, 01:17 AM
#3
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.
-
Jan 26th, 2012, 01:00 PM
#4
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.
-
Jan 26th, 2012, 07:53 PM
#5
Re: populate ListView using a thread
 Originally Posted by moeur
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.
 Originally Posted by moeur
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.
-
Jan 26th, 2012, 08:17 PM
#6
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.
-
Jan 26th, 2012, 08:19 PM
#7
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?
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Jan 26th, 2012, 09:02 PM
#8
Re: populate ListView using a thread
 Originally Posted by moeur
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?
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|