Question

I have a .Net 4.0 class that has a custom event handler (StatusChanged). I am trying to consume this class via a thread. I don't have the luxury of using BackgroundWorker because libraries I use do not support multi thread apartment state.

One of the parameters in the StatusChanged delegate is a string.

In the UI, a button click initializes a thread that uses my class.

I am trying to make it so a UI control (WPF) gets updated to be the StatusChanged string parameter when StatusChanged event fires. My understanding is that Dispatcher.Invoke can do this, but I am not having any luck figuring out how to pull the string out of it.

Essentially, I am trying to get the equivalent of BackgroundWorker.ProgressChanged (with the ProgressChangedEventArgument.UserState).

Here is some scaled-down code to demonstrate what I'm getting at:

Class with event handler:

public class MyClass
{
    public event EventHandler StatusChanged;

    private void alertStatus(string message)
    {
        if(StatusChanged == null)
            return;
        this.StatusChanged(message, new EventArgs());
    }

    public void DoStuff()
    {
        ///Do Stuff 1
        alertStatus("Done with stuff #1");

        ///Do Stuff 2            
        alertStatus("Done with stuff #2");
    }
}

So a button's .Click event would do something like this:

private void buttonClicked(object sender, EventArgs e)
{
    Thread t = new Thread(doWork);
    t.Start();
}

private void doWork()
{
    MyClass class = new MyClass();
    class.StatusChanged += ...
    class.DoStuff();
    ///As StatusChanged fires within MyClass, a UI Textbox would get updated
}
Was it helpful?

Solution

It's easiest just to use a lambda expression:

private void doWork()
{
    // Name changed to avoid it being a keyword
    MyClass clazz = new MyClass();
    clazz.StatusChanged += (sender, args) => {
        string message = (string) sender; // This is odd
        Action action = () => textBox.Text = message;
        Dispatcher.Invoke(action);
    };
    clazz.DoStuff();
}

It's very odd using a string as the sender of an event though - it would be better to use a subclass of EventArgs, and make that store the message - then make your event use EventHandler<T> instead of just EventHandler.

The above code is slightly confusing because you're subscribing to the event with one delegate, then passing another to Dispatcher.Invoke - hence the two lambda expressions (one inside another). You could always use a method group instead, at least for the outer one:

private void doWork()
{
    // Name changed to avoid it being a keyword
    MyClass clazz = new MyClass();
    clazz.StatusChanged += HandleStatusChange;
    clazz.DoStuff();
}

private void HandleStatusChange(object sender, EventArgs e)
{
    string message = (string) sender; // This is odd
    Action action = () => textBox.Text = message;
    Dispatcher.Invoke(action);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top