possible to create list from Listview?

R

Rich P

I need to pass a list to a backgroundworkder - specifically from a
listview control. I have attempted the following:

List<ListView> myLvw
foreach(ListViewItem lvw in ListView1)
myLvw.Add(lvw)

but this is not working. I need to create a list that will contain the
equivalent contents of ListView1. How can I do this? I also tried
passing ListView1 to the background worker but got a crossthread error
message.

here is the basic scenario:

...btn1_Click(...)
{
//populate a listview control
//create a list that is equivalent to the listview control
//call backgroundworker(list)
}

...backgroundWorker1_DoWork(...)
{
List<ListView>myLvw = <ListView>e.Argument;
...
}

Is it possible to pass a listview type list to the backgroundworker or
should I just create and dimension a multi-dimensional string array to
pass to the backgroundworker?

Thanks,

Rich
 
P

Peter Duniho

I need to pass a list to a backgroundworkder - specifically from a
listview control. I have attempted the following:

List<ListView> myLvw
foreach(ListViewItem lvw in ListView1)
myLvw.Add(lvw)

but this is not working.

You should avoid writing simply "this is not working". When you have a
problem, state clearly and specifically what the problem is. In what way
does it not work? Do you get an error? Output with the wrong results?

As far as this specific example goes, the ListViewItem instances in the
ListView are in the collection returned by the ListView.Items property.
It's that collection you need to enumerate, not the ListView itself.
Furthermore, if you want to enumerate the ListViewItem instances and add
them to a List<T>, then you need to use "ListViewItem" as the type
parameter T, not "ListView".

In actuality, you probably don't want a ListViewItem as your list item
either. Instead, you want to enumerate the ListViewItems in the
ListView.Items collection, and extract whatever the pertinent information
for each ListViewItem is (e.g. the Text property, or perhaps multiple
subitem values...this depends on what you're doing in the
BackgroundWorker.DoWork event handler).

Once you've created the data structure you want to use in your DoWork
event handler, you can pass it in a variety of ways:

-- Create a new helper class that contains the DoWork event handler as
an instance method, and in which is contained all the data your event
handler will need. The event handler itself will then have direct access
to the information.

-- Pass a reference to the RunWorkerAsync() method, and have the event
handler cast the DoWorkEventArgs.Argument property back to the appropriate
type.

-- Make the DoWork event handler an anonymous method that simply calls
the actual event handler with whatever argument(s) you want.

I actually prefer the third option; it basically combines the first two,
while having the compiler do all the "heavy lifting" (such as it is). The
anonymous method "captures" variables used by causing a hidden class to be
created and used by the compiler, as in the first option, and by passing
the values of those captured variables to the actual method that does the
work as typed arguments, you accomplish the second option, but without
having to explicitly cast an Object value back to some other type.

But any of the three would work fine.

Pete
 
R

Rich P

Hi Pete,

Thank you as always for getting back to me. The listview control
contains (one blank column) an item enumerator column (row number
column), filesize column, filename column for a list of .jpg files.
Here is what I have attempted along the lines of your suggestion (for
creating a list - still having a problem here):

List<ListView.ListViewItemCollection> myLvw;

foreach (ListViewItem lvw in lstView1.Items)
myLvw.Add(lvw);

And here are the associated error messages:
Error 1: The best overloaded method match for
'System.Collections.Generic.List<System.Windows.Forms.ListView.ListViewI
temCollection>.Add(System.Windows.Forms.ListView.ListViewItemCollection)
' has some invalid arguments
<<Error 2: Argument '1': cannot convert from
'System.Windows.Forms.ListViewItem' to
'System.Windows.Forms.ListView.ListViewItemCollection'
Any suggestions appreciated how I can get around these errors.

Rich
 
R

Rich P

I think I solve the problem. Here is what I did:

...btn1_click(...)
{
...
List<ListViewItem> myLvw = new List<ListViewItem>();
foreach (ListViewItem lvw in lstView1.Items)
myLvw.Add(lvw);
backgroundWorker1.RunWorkerAsync(myLvw);
...
}

...dowork(...)
{
...
List<ListViewItem> Lvw = e.Argument as List<ListViewItem>;
...
for (int i = 0; i < Lvw.Count; i++)
{
...
fileSize = Convert.ToInt32(Lvw.SubItems[2].Text);
fileName = Lvw.SubItems[3].Text;
j = Convert.ToInt32((i + 1) * 100 / Lvw.Count);
backgroundWorker1.ReportProgress(j, (i + 1).ToString() + ") " +
fileName);
...
}

The routine successfully downloaded a list of .jpg files from my ftp
server.

Rich
 
P

Peter Duniho

[...]
..dowork(...)
{
..
List<ListViewItem> Lvw = e.Argument as List<ListViewItem>;
..
for (int i = 0; i < Lvw.Count; i++)
{
..
fileSize = Convert.ToInt32(Lvw.SubItems[2].Text);
fileName = Lvw.SubItems[3].Text;
j = Convert.ToInt32((i + 1) * 100 / Lvw.Count);
backgroundWorker1.ReportProgress(j, (i + 1).ToString() + ") " +
fileName);
..
}

The routine successfully downloaded a list of .jpg files from my ftp
server.


IMHO, because ListViewItem is related to the GUI object ListView, you
should avoid objects of that type from your BackgroundWorker, just as you
avoid accessing the ListView object itself. As ListViewItem isn't a
Control, nor does it have a direct dependency on a window handle or the
thread that created it, it is theoretically safe to use it the way you're
using it.

But, as ListViewItem _does_ have its own dependency on the ListView
itself, and thus things you do to the ListViewItem could in fact propagate
back to the ListView from your BackgroundWorker, it's better to avoid the
situation altogether by isolating the use of ListViewItem instances to
your main GUI thread. Yes, the current usage is safe, but from a
maintenance point of view, it's very hard to guarantee that no one will
ever modify the code in an unsafe way.

Given how it appears you're using it, you could simply create two arrays,
one to contain the size string and one to contain the filename string.
The "count" of course can be obtained from either array. Alternatively,
create a separate struct to hold both the size and filename, and pass a
collection (e.g. array) of that to your DoWork event handler.

At the very least, change your use of "as" to a straight cast. You should
only ever use "as" when a failure to cast is valid; i.e. you've got code
that checks for null after the "as" and does something useful. Using "as"
when failure is not allowed simply obfuscates any error that might occur
if and when there's a bug that does cause a failure.

Finally, if you do continue to use the ListViewItem instances directly in
your DoWork event handler, there's really no reason to copy them from the
ListView.Items collection. Just pass a reference to that collection
itself, having retrieved the value from the ListView object in your main
GUI thread. After all, not being a Windows control the
ListViewItemCollection is just as independent of the ListView object
itself as the ListViewItems are. I don't see any reason to go to the
trouble of isolating the ListViewItemCollection from your worker thread
when you're still going to use the ListViewItems there; the two data
structures are basically the same with respect to their thread affinity,
and carry similar cross-thread risks (i.e. low risks, possible to be used
safely, but not completely risk-free).

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top