2

Let's say I created an object O on the thread T. How can I get, from inside object O the thread T and invoke a method on that thread?. This way, it won't be necessary for the form that created the object to to this:

    private void ChangeProgress(int value)
    {
        progressBar1.Value = value;
    }

    void FD_ProgressChanged(object sender, DownloadEventArgs e)
    {
        if (InvokeRequired)
        {
            Invoke(new Action<int>(ChangeProgress), new object[] { e.PercentDone });
        }
        else ChangeProgress(e.PercentDone);
    }

which is just ugly and requires whoever uses the object to either figure out which events are raised on the same thread that created the object and which are not and add the if(InvokeRequired)...else code on the ones that are not, or just add the code on every single event handler. I think it would be more elegant if the object itself takes care of invoking the event on the right thread. Is this possible?

Juan
  • 15,274
  • 23
  • 105
  • 187
  • Actually I'll listen to Hans and rewrite the class with a `BackgroundWorker`. I can wrap around the `ReportProgress` to notify whatever I want to. – Juan Dec 24 '10 at 21:13

4 Answers4

4

Use the BackgroundWorker class. It takes care of all this. Note the ReportProgress event.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
2

You are going to have to track it yourself like

class Foo {
    private readonly Thread creatingThread;

    public Foo() {
        this.creatingThread = Thread.CurrentThread;
    }
}

If you don't do that, there is no way to know. But the fact that you are doing this is a smell. Consider using a BackgroundWorker.

jason
  • 236,483
  • 35
  • 423
  • 525
1

There are a few thing you need to consider:

  • You will need to keep a reference in Object O of a thread that it was created in. Probably in a constructor using Thread.Current static property.
  • That thread will need to have a SynchronizationContext associated with it. (Generally, UI threads have it. And its not easy to create one for a custom thread you created.)
  • To invoke a method on that thread, you will need to use Send() or Post() methods on that thread's SynchronizationContext.
decyclone
  • 30,394
  • 6
  • 63
  • 80
  • How do you get the `SynchronizationContext`? I know the thread is a UI thread so it must have one but how do you get it? – Juan Dec 22 '10 at 19:23
1

Found a nice solution at http://www.codeproject.com/KB/threads/invoke_other_way.aspx

And here's is my generic version:

    private void RaiseEventAsync(Delegate handler, object e)
    {
        if (null != handler)
        {
            List<Delegate> invocationList = handler.GetInvocationList().ToList();

            foreach (Delegate singleCast in invocationList)
            {
                System.ComponentModel.ISynchronizeInvoke syncInvoke =
                           singleCast.Target as System.ComponentModel.ISynchronizeInvoke;
                try
                {
                    if ((null != syncInvoke) && (syncInvoke.InvokeRequired))
                        syncInvoke.Invoke(singleCast,
                                      new object[] { this, e });
                    else
                        singleCast.Method.Invoke(singleCast.Target, new object[] { this, e });
                }
                catch
                { }
            }
        }
    }

And this is how you would use it:

    protected void OnProgressChanged(DownloadEventArgs e)
    {
        RaiseEventAsync(ProgressChanged, e);
    }

This takes care of my problem without needing to use a BackgroundWorker that is not always wanted (like on my case, where I'm subclassing a class that already uses a different threading object).

Juan
  • 15,274
  • 23
  • 105
  • 187
  • 1
    Excellent! That way, your code will remain tied to WinForms forever! Or... you could use the Task Parallel Library and maintain UI-independence. – Stephen Cleary Dec 22 '10 at 21:23