On Thu, 17 May 2007 08:23:26 -0700, deciacco <a@awrote:
[...]
I was thinking something like this:
************
private delegate void setScriptStatus ImageDelegate(i nt imgIndex, int
listItemIndex);
this.listviewPr ogress.Invoke(n ew
setScriptStatus ImageDelegate(s etScriptStatusI mage), new object[] { 0,
lvi.Index });
private void setScriptStatus Image(int imgIndex, int listItemIndex)
{
this.listviewPr ogress.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...thou gh 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 setScriptStatus ImageDelegate(i nt imgIndex, int
listItemIndex);
this.listviewPr ogress.Invoke(n ew
setScriptStatus ImageDelegate(s etScriptStatusI mage), new object[] { 0,
iitem });
private void setScriptStatus Image(int imgIndex, int listItemIndex)
{
this.listviewPr ogress.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.listviewPr ogress.Invoke(n ew
setScriptStatus ImageDelegate(s etScriptStatusI mage), new object[] { 0,
iitem });
Thread.Sleep(20 00);
this.listviewPr ogress.Invoke(n ew
setScriptStatus ImageDelegate(s etScriptStatusI mage), 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