none
Cross-thread operation not valid: Control ‘’ accessed from a thread other than the thread it was created on

    Question

  • public works() //Constructor
     public Works()
            {
     ObjWatcher.EventArrived += new WMIEventArrived(ObjWatcher_EventArrived);
    }
    void ObjWatcher_EventArrived(object sender, WMIEventArgs e)
            {
     this.tStripNewProject.Enabled = true;
     this.tStripOpenProject.Enabled = true;
     this.tStripUpgradeProjects.Enabled = true;
    }

    In above code tStripNewproject only enabled during this delegate call. Following that 2 toolstrip not working. Once tStripNewproject is enabled, code comes out. Can anyone help regarding?

    • Edited by rnjnagu Thursday, October 25, 2012 9:10 AM
    Thursday, October 25, 2012 7:34 AM

Answers

  • Controls on a windows form can only be accessed by the thread that owns them (GUI).  This isn't an issue for button clicks, keys, etc. because the GUI thread is the one issuing the events.

    On the other hand, your WMI events are being fired from a different thread.  To access forms controls, from another thread you need to use control.Invoke()

    void ObjWatcher_EventArrived(object sender, WMIEventArgs e)
    {
        tStripNewProject.Invoke(new MethodInvoker(() => { tStripNewProject.Enabled = true; }));
        tStripOpenProject.Invoke(new MethodInvoker(() => { tStripOpenProject.Enabled = true; }));
        tStripUpgradeProjects.Invoke(new MethodInvoker(() => { tStripUpgradeProjects.Enabled = true; }));
    }
    

    Since all the controls are on the same form, you can combine these to a single invoke and call it using the form instance rather than a control instance: Something like

    void ObjWatcher_EventArrived(object sender, WMIEventArgs e)
    {
        yourForm.Invoke(new MethodInvoker(() =>
        {
            tStripNewProject.Enabled = true;
            tStripOpenProject.Enabled = true;
            tStripUpgradeProjects.Enabled = true;
        }));
    }
    



    This signature unintentionally left blank.

    Thursday, October 25, 2012 11:31 AM
  • @nick

    It doesn't really matter which control you use to call `Invoke` on here as they all have the same UI thread.  In almost every single application there will only be one UI thread; it's considered bad practice to have multilevel UI threads, generally speaking.

    You could, for example, do this and it would be just fine:

    tStripNewProject.Invoke(new MethodInvoker(() =>
        {
            tStripNewProject.Enabled = true;
            tStripOpenProject.Enabled = true;
            tStripUpgradeProjects.Enabled = true;
        }));


    Or, since `this` is clearly the form, you could just use `this`'s invoke method:

        Invoke(new MethodInvoker(() =>
        {
            tStripNewProject.Enabled = true;
            tStripOpenProject.Enabled = true;
            tStripUpgradeProjects.Enabled = true;
        }));


    My point here is just that you don't need to worry so much about which control you use to call invoke on, in 99% of situations any control that you use will work, and you almost need to *try* to end up in a situation where it won't work.

    Also there really isn't any reason to call invoke three times in a row like you do in that first code sample.  There's some overhead associated with marshaling to the UI thread; it's much better to just call it once and do all of the UI tasks within the single call.

    Thursday, October 25, 2012 3:05 PM

All replies

  • Controls on a windows form can only be accessed by the thread that owns them (GUI).  This isn't an issue for button clicks, keys, etc. because the GUI thread is the one issuing the events.

    On the other hand, your WMI events are being fired from a different thread.  To access forms controls, from another thread you need to use control.Invoke()

    void ObjWatcher_EventArrived(object sender, WMIEventArgs e)
    {
        tStripNewProject.Invoke(new MethodInvoker(() => { tStripNewProject.Enabled = true; }));
        tStripOpenProject.Invoke(new MethodInvoker(() => { tStripOpenProject.Enabled = true; }));
        tStripUpgradeProjects.Invoke(new MethodInvoker(() => { tStripUpgradeProjects.Enabled = true; }));
    }
    

    Since all the controls are on the same form, you can combine these to a single invoke and call it using the form instance rather than a control instance: Something like

    void ObjWatcher_EventArrived(object sender, WMIEventArgs e)
    {
        yourForm.Invoke(new MethodInvoker(() =>
        {
            tStripNewProject.Enabled = true;
            tStripOpenProject.Enabled = true;
            tStripUpgradeProjects.Enabled = true;
        }));
    }
    



    This signature unintentionally left blank.

    Thursday, October 25, 2012 11:31 AM
  • @nick

    It doesn't really matter which control you use to call `Invoke` on here as they all have the same UI thread.  In almost every single application there will only be one UI thread; it's considered bad practice to have multilevel UI threads, generally speaking.

    You could, for example, do this and it would be just fine:

    tStripNewProject.Invoke(new MethodInvoker(() =>
        {
            tStripNewProject.Enabled = true;
            tStripOpenProject.Enabled = true;
            tStripUpgradeProjects.Enabled = true;
        }));


    Or, since `this` is clearly the form, you could just use `this`'s invoke method:

        Invoke(new MethodInvoker(() =>
        {
            tStripNewProject.Enabled = true;
            tStripOpenProject.Enabled = true;
            tStripUpgradeProjects.Enabled = true;
        }));


    My point here is just that you don't need to worry so much about which control you use to call invoke on, in 99% of situations any control that you use will work, and you almost need to *try* to end up in a situation where it won't work.

    Also there really isn't any reason to call invoke three times in a row like you do in that first code sample.  There's some overhead associated with marshaling to the UI thread; it's much better to just call it once and do all of the UI tasks within the single call.

    Thursday, October 25, 2012 3:05 PM
  • Yes, you can use a control to do the work, but the form is the better choice:

    It's easier to maintain.  If you delete or rename that control, you need to wire up a different one.  On the other hand if you delete the whole form... all the controls go with it.

    It's also a better choice conceptually.  When you need to report a fire do you call a fireman on his cell phone and ask him to wake up his buddies?  Or do you call the fire department and let them figure out who's on duty, who's closest, etc.

    As for 'this' being the form... I cry 'likely' but not 'obvious'

    :-)  Cheers!


    This signature unintentionally left blank.


    • Edited by Nick F. _ Friday, October 26, 2012 5:53 PM
    Friday, October 26, 2012 5:52 PM
  • Yes, you can use a control to do the work, but the form is the better choice:

    It's easier to maintain.  If you delete or rename that control, you need to wire up a different one.  On the other hand if you delete the whole form... all the controls go with it.

    It's also a better choice conceptually.  When you need to report a fire do you call a fireman on his cell phone and ask him to wake up his buddies?  Or do you call the fire department and let them figure out who's on duty, who's closest, etc.

    As for 'this' being the form... I cry 'likely' but not 'obvious'

    "this" is clearly a UI control of some sort.  It could be a form, it could be a user control, or whatever, but clearly it's some sort of Control, so "this" will almost certainly have an "Invoke" method that can be used.

    For the rest, when renaming a control you should really use the visual studio refactor, which will change all instances for you, so that would be fine.  If you deleted one of the three controls, and that happened to be the one using "invoke" then yes, it would cause a syntax error needing fixing, but syntax errors are pretty easy to fix.  Also note that if "this" isn't the form then the form being access is some other form which could be deleted or renamed, just like the other controls.

    As for the concept, they are all doing *exactly the same thing*.  Every controls has an internal reference to a SynchronizationContext.  All of those controls have the same reference to the same object, and they all perform the same function on that referenced object when you call "invoke".

    Friday, October 26, 2012 6:07 PM
  • If you are using WPF you need to use Dispatcher class.
    Friday, October 26, 2012 6:22 PM
  • No arguments on what works. 

    But I still have to respectfully disagree when viewed from a design and maintainability standpoint. 

    The controls live in a container--be it form, user control, widget, whatever.  The container requires at least some rudimentary knowledge of what it contains.  On the other hand the contained items don't need to knowledge of one another unless there's a design constraint not present in the original post.

    Having ButtonX invoke something on ButtonX and ButtonY isn't conceptually as sound as having the container invoke something on ButtonX and ButtonY.  Given that the OP has the option of doing it either way, I still believe it's better to go with the latter.

    N-


    This signature unintentionally left blank.

    Saturday, October 27, 2012 3:12 PM
  • No arguments on what works. 

    But I still have to respectfully disagree when viewed from a design and maintainability standpoint. 

    The controls live in a container--be it form, user control, widget, whatever.  The container requires at least some rudimentary knowledge of what it contains.  On the other hand the contained items don't need to knowledge of one another unless there's a design constraint not present in the original post.

    Having ButtonX invoke something on ButtonX and ButtonY isn't conceptually as sound as having the container invoke something on ButtonX and ButtonY.  Given that the OP has the option of doing it either way, I still believe it's better to go with the latter.

    That's the problem, you're conceptually thinking that buttonX is doing the invoking.  It's not.  The synchronization context is what's doing the invoking, the button is just telling you what the synchronization context is.

    Really, it would have been better API design if, rather than giving controls an Invoke method it instead required you to do:

    control.Context.Post(delegate);

    The it would be clear that the control isn't actually invoking, it's just asking it's parent "Context" to do the invoking.  You can consider the "context" to be essentially a property of the top of the control hierarchy, but that it will automatically walk it's way up to the top on it's own.  Therefore, both conceptually and practically, it makes sense to just grab any old control and use it's Invoke method, so long as you know that it's a part of the same "tree".  Since the invoke method is walking it's way to the top anyway, the relationship between the control actually invoking and controls used within the invoke method is irrelivant.

    Now, having said that, do I actually want the API changed, no.  The way it is is quite convenient, it's just a poor conceptual representation of what it's doing.  I don't think the better conceptualization is worth the loss in convenience when it's a method that is used so frequently.

    Monday, October 29, 2012 2:06 PM