[...]
I was thinking something like this:
************
private delegate void setScriptStatusImageDelegate(int imgIndex, int
listItemIndex);
this.listviewProgress.Invoke(new
setScriptStatusImageDelegate(setScriptStatusImage), new object[] { 0,
lvi.Index });
private void setScriptStatusImage(int imgIndex, int listItemIndex)
{
this.listviewProgress.Items[listItemIndex].ImageIndex = imgIndex;
}
************
The problem is, I still get the cross-thread error on lvi.Index.
I thought I should be able to pass in lvi.Index since I used a
delegate to get lvi.
Nope. Sorry.
Going through Invoke (note that it's the Invoke
that's important, not the delegate...though Invoke does of course
require a delegate) is what prevents the cross-thread problem, and it
applies only during the actual execution of Invoke. You get the same
object back via Invoke as you would calling directly, and all the same
rules apply to that object.
(By the way, there *are* mechanisms that actually do convert certain
kinds of objects from one execution environment to another, whether
that's cross-thread, cross-process, cross-network, etc...it's known as
"marshaling". So it wasn't unreasonable of you to think maybe what you
wanted to do was possible. It just turns out that in this case, what's
being "marshaled" is the code execution, not any of the data being used).
You can get the reference to the ListViewItem and handle that safely in
the thread, but any operations on it have to follow the same rules as
apply to the ListView object itself. So, you can Invoke to get the
reference to it, and then you can pass that reference back to the UI
thread via Invoke for doing something else, but you can't actually *do*
anything with the reference on the worker thread.
Fortunately, none of this is a big problem. You may have already
figured out the solution, but just in case...
If you want to pass an index rather than the ListViewItem itself, you
can just change the code to look like this:
private delegate void setScriptStatusImageDelegate(int imgIndex, int
listItemIndex);
this.listviewProgress.Invoke(new
setScriptStatusImageDelegate(setScriptStatusImage), new object[] { 0,
iitem });
private void setScriptStatusImage(int imgIndex, int listItemIndex)
{
this.listviewProgress.Items[listItemIndex].ImageIndex = imgIndex;
}
and then the loop looks like this:
for (int iitem = 0; iitem < citem; iitem++)
{
// Use Invoke to actually change the image index
this.listviewProgress.Invoke(new
setScriptStatusImageDelegate(setScriptStatusImage), new object[] { 0,
iitem });
Thread.Sleep(2000);
this.listviewProgress.Invoke(new
setScriptStatusImageDelegate(setScriptStatusImage), new object[] { 0,
iitem });
}
The main problem being that you are now even more exposed to the
possibility of the ListView changing between calls. Not that that
problem didn't exist before, but at least if there was a bug, you would
still always update the icon for the same ListViewItem (though by the
time you get to updating it, it would be possible that it wasn't the
ListViewItem corresponding to the processing you're doing, depending on
how you correlate your ListViewItem instances when the processing
iteration). With the above code, if there's a bug you could actually
wind up setting the icon 0 for one ListViewItem and then setting the
icon 1 for a different ListViewItem later.
Obviously the best solution is to not have bugs where you modify or
otherwise lose correlation with the ListView while it's still in use.
Just something to be aware of. The two versions of the code aren't
functionally equivalent, but as long as you understand the differences,
I think that's fine.
Pete