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."?

10,729

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.

Share:
10,729
Kevin
Author by

Kevin

SOreadytohelp

Updated on June 29, 2022

Comments

  • Kevin
    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

    prgAll.Maximum = lbFolders.SelectedItems.Count;
    I get the error. However, here is the part I don't get. If I comment out that line, the very next line
    foreach (string dir in lbFolders.SelectedItems)
    executes just fine.

    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
    Kevin over 15 years
    nope, wrong answer. I broke it up and the error is happening when getting the Count value, not when setting the Maximum value
  • Jon B
    Jon B over 15 years
    @Kevin Any chance you're creating prgAll and lblFolders from different threads?
  • Jon B
    Jon B over 15 years
    @Kevin using prgAll.Invoke instead of this.Invoke might make that work for you.
  • Kevin
    Kevin over 15 years
    nope, wrong answer. I broke it up and the error is happening when getting the Count value, not when setting the Maximum value
  • Matt Warren
    Matt Warren over 15 years
    OOpps, sorry, didn't mean to remove your comment
  • Kevin
    Kevin over 15 years
    Thank 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
    Kevin over 15 years
    No 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
    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
    OregonGhost over 15 years
    +1 for the remark about inner state. Unfortunately Kevin doesn't like your answer, even though it's true.
  • Kevin
    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
    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
    Kevin over 15 years
    Actually, his edited answer makes much more sense. Changed my -1 to a +1
  • Kevin
    Kevin over 15 years
    Of course, why M$ designed it like that still doesn't make sense, but that isn't Lykathea's problem
  • OregonGhost
    OregonGhost over 15 years
    I think the reason is that they only test for inter-thread calls when it really matters. Kind of an optimization.
  • ScottCher
    ScottCher over 15 years
    Regardless 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
    Matt Warren over 15 years
    I 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
    Kevin over 15 years
    I 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
    fIwJlxSzApHEZIl over 11 years
    This gave me "No overload for 'function name' matches delegate 'System.Threading.ThreadStart'
  • fIwJlxSzApHEZIl
    fIwJlxSzApHEZIl over 11 years
    This 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
    fIwJlxSzApHEZIl over 11 years
    Figured it out! You add a second parameter to invoke to pass your arguments. Like this: this.BeginInvoke(new DoThreadedGoodManualType(DoThreadedGoodManual), new Object[] {param1, param2};));