views:

62

answers:

3

Hi all,

Here is my problem, I have a class which have a object who throw an event and in this event I throw a custom event from my class. But unfortunately the original object throw the event from another thread and so my event is also throw on another thread. This cause a exception when my custom event try to access from controls.

Here is a code sample to better understand :

class MyClass
{
    // Original object
    private OriginalObject myObject;

    // My event
    public delegate void StatsUpdatedDelegate(object sender, StatsArgs args);
    public event StatsUpdatedDelegate StatsUpdated;

    public MyClass()
    {
        // Original object event
        myObject.DoSomeWork();
        myObject.AnEvent += new EventHandler(myObject_AnEvent);
    }

    // This event is called on another thread while myObject is doing his work
    private void myObject_AnEvent(object sender, EventArgs e)
    {
        // Throw my custom event here
        StatsArgs args = new StatsArgs(..........);
        StatsUpdated(this, args);
    }
}

So when on my windows form I call try to update a control from the event StatsUpdated I get a cross thread exception cause it has been called on another thread.

What I want to do is throw my custom event on the original class thread, so control can be used within it.

Anyone can help me ?

+3  A: 

You may take a look at the InvokeRequired/Invoke pattern.

Before trying to update some control you check if invoke is required and use the Invoke method which will take care of marshaling the call to the thread that has created this control:

Control ctrlToBeModified = // 
if (ctrlToBeModified.InvokeRequired)
{
    Action<Control> del = (Control c) => 
    { 
        // update the control here
    };
    ctrlToBeModified.Invoke(del, ctrlToBeModified);
}

UPDATE:

private void myObject_AnEvent(object sender, EventArgs e)
{
    // Throw my custom event here
    StatsArgs args = new StatsArgs(..........);
    Control control = // get reference to some control maybe the form or 'this'
    if (control.InvokeRequired)
    {
        Action<Control> del = (Control c) => 
        { 
            // This will invoke the StatsUpdated event on the main GUI thread
            // and allow it to update the controls
            StatsUpdated(this, args);
        };
        control.Invoke(del);
    }
}
Darin Dimitrov
Invoke is a method that can be called from a control, here what I want to do is directly throw my event on the original thread (from my class). So in my windows form I don't have to care about invoke. I can directly do something like "progressbar.value = arg.progress;
Karnalta
In this case at the moment you invoke the event (not specified in your snippet) you check for invoke required and call the event inside the delegate paased to the Invoke method.
Darin Dimitrov
I am not sure to understand, you are talking about my event or the originalObject event ?
Karnalta
I am talking about the `StatsUpdated` event as this is the one that updates the UI I presume. So instead of calling directly `StatsUpdated(this, args);` inside the `myObject_AnEvent` method put this call into a `Control.Invoke`.
Darin Dimitrov
Can you give me a sample ? I don't understand what to do Control has no Invoke() method for me.
Karnalta
Ok I understand but I can't give any control reference, I am in an class within a DLL library. This method would requiere my class to ask a control within his contructor parameters, which is not really what I want.
Karnalta
If you don't want to pass a control reference to your class library you will have to do this inside the windows forms application that is using this class library and that is registering the `StatsUpdated` event. The application will have reference to the main form.
Darin Dimitrov
Yes I think you are right, that's certainly why the method describe in the article Adam has posted doesn't work in my case. There is no way for an external DLL to know the thread used by my application and send the event on it I suppose.
Karnalta
A: 

I am not sure if this is the case, but if it is safe you may add to your form constructor:

Control.CheckForIllegalCrossThreadCalls= false;

This is the "easy way" that only applies in some limited scenarios. In my case works like a charm.

Daniel Dolz
Yes but this is also a solution used on the windows form, I'd like to correct my problem inside my class, before throwing my custom event. So I keep my DLL as simple as possible to use inside a windows form.
Karnalta
A: 

It looks like you need to look into the SynchronizationContext or AsyncOperation classes.

http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx

Adam Maras
Yes I am currently reading this article, it seem to be what I am looking for.
Karnalta
I just tried with both methods in this article but I still get the cross-threading error when updating my progressbar. It's strange cause the method seem pretty logical.
Karnalta