Why am I getting this error:"Cross-thread operation not valid: Control lbFolders accessed from a thread other than the thread it was created on."?
Solution 1
prgAll.Maximum = lbFolders.SelectedItems.Count;
On that line you perform an assignment (set/add), which by default is not thread-safe.
On the second line it's just a get operation, where thread-safety merely doesn't matter.
EDIT: I don't mean access to the prgAll element.
Accessing the Count property changes the internal state of the ListBox inner collection, that is why it throws the exception.
Solution 2
You can't access GUI elements from a separate thread. Use a delegate to make the change.
eg.
lblStatus.Invoke((Action)(() => lblStatus.Text = counter.ToString()));
or older skool:
lblTest.Invoke((MethodInvoker)(delegate()
{
lblTest.Text = i.ToString();
}));
I've got a blog post on how to do this in all the .Net releases here.
Solution 3
The Count property of SelectedItems is not thread-safe, so you can't use it cross-thread.
Solution 4
You're trying to write to a control from a thread other than the main thread. Use Invoke or BeginInvoke.
void SetMax()
{
if (prgAll.InvokeRequired)
{
prgAll.BeginInvoke(new MethodInvoker(SetMax));
return;
}
prgAll.Maximum = lbFolders.SelectedItems.Count;
}
Solution 5
You can't touch a GUI object from a thread that isn't the main GUI thread. See here for more details and the solution.
Comments
-
Kevin almost 2 years
This is baffling me, maybe somebody can shine the light of education on my ignorance. This is in a C# windows app. I am accessing the contents of a listbox from a thread. When I try to access it like this
I get the error. However, here is the part I don't get. If I comment out that line, the very next lineprgAll.Maximum = lbFolders.SelectedItems.Count;
executes just fine.foreach (string dir in lbFolders.SelectedItems)
Edit: As usual, my communication skills are lacking. Let me clarify.
I know that accessing GUI items from threads other than the ones they were created on causes problems. I know the right way to access them is via delegate.
My question was mainly this: Why can I access and iterate through the SelectedItems object just fine, but when I try to get (not set) the Count property of it, it blows up.
-
Kevin over 15 yearsnope, wrong answer. I broke it up and the error is happening when getting the Count value, not when setting the Maximum value
-
Jon B over 15 years@Kevin Any chance you're creating prgAll and lblFolders from different threads?
-
Jon B over 15 years@Kevin using prgAll.Invoke instead of this.Invoke might make that work for you.
-
Kevin over 15 yearsnope, wrong answer. I broke it up and the error is happening when getting the Count value, not when setting the Maximum value
-
Matt Warren over 15 yearsOOpps, sorry, didn't mean to remove your comment
-
Kevin over 15 yearsThank you, this is the only answer which even begins to answer the question. So, the SelectedItems object is thread-safe, but its property isn't? That doesn't make sense to me. Not saying you are wrong, just that it doesn't make sense.
-
Kevin over 15 yearsNo problem, I'll just re-add it. Your answer doesn't make sense because setting the value of the prgAll.Maximum isn't blowing up, neither does it blow up on accessing the SelectItems itself, just on the selecteditems.count
-
OregonGhost over 15 years@Kevin: matt's answer makes perfect sense. While you can't generally access GUI elements from another thread, not all properties or methods suffer from this. The rule is quite simple: Don't touch GUI objects in a thread other than the GUI thread. See Echostorms answer for how to do it right.
-
OregonGhost over 15 years+1 for the remark about inner state. Unfortunately Kevin doesn't like your answer, even though it's true.
-
Kevin over 15 years@OregonGhost: no, your answer makes sense, at least more sense. His doesn't make sense because he's telling me I can't do something which I obviously can.
-
OregonGhost over 15 years@Kevin: Lykathea explains why this is the way it is. Maybe you could implement it in another way, but that's how the .NET team did it. Just never access any GUI elements from another thread and you're fine.
-
Kevin over 15 yearsActually, his edited answer makes much more sense. Changed my -1 to a +1
-
Kevin over 15 yearsOf course, why M$ designed it like that still doesn't make sense, but that isn't Lykathea's problem
-
OregonGhost over 15 yearsI think the reason is that they only test for inter-thread calls when it really matters. Kind of an optimization.
-
ScottCher over 15 yearsRegardless of it making sense or not, its clear that cross-threading is the issue and you should be invoking back to the gui thread to access those properties. To see if something is thread-safe, look it up in the MSDN library - it clearly states thread-safety.
-
Matt Warren over 15 yearsI think it goes back to a Win32 issue (or design decision), which is still there in WinForms because it uses Win32/MFC under the hood
-
Kevin over 15 yearsI actually did look up both the SelectedItems object and the Count property on MSDN, I didn't see anything on the "thread-safeness" of either.
-
fIwJlxSzApHEZIl over 11 yearsThis gave me "No overload for 'function name' matches delegate 'System.Threading.ThreadStart'
-
fIwJlxSzApHEZIl over 11 yearsThis post has helped: codeproject.com/Articles/20081/… but it doesn't explain how to call a function with parameters. My 'RunMe' function has two parameters and writing the delegate without them throws 'System.Reflection.TargetParameterCountException' Adding them as part of the delegate gives the compiler error: 'Method Name Expected'
-
fIwJlxSzApHEZIl over 11 yearsFigured it out! You add a second parameter to invoke to pass your arguments. Like this: this.BeginInvoke(new DoThreadedGoodManualType(DoThreadedGoodManual), new Object[] {param1, param2};));