1

I have a WPF application with lots of user controls. One of these controls also uses a 3rd party DLL that watches an external system and produces events. I subscribe to those events and handle them with something like this:

public class ControlClassD
{
    private 3rdPartyEventSource _3rdPartyEventSource = new 3rdPartyEventSource();

    public ControlClassD()
    {
        _3rdPartyEventSource.NewEvent += _3rdPartyEventSource_NewEvent;
        _3rdPartyEventSource.StartMakingEventsWhenSomethingHappens();
    }

    private void _3rdPartyEventSource_NewEvent(object o)
    {
        InstanceOfControlClassA.doSomethingWith(o);
        InstanceOfControlClassB.doSomethingWith(o);
        InstanceOfControlClassC.doSomethingWith(o);
    }
}

All of the InstanceOfControlClassx were instantiated by whatever thread runs the _Loaded event handler in the MainWindow class at startup.

The thread executing the handler is one created by the 3rdPartyEventSource and has no access to all these things (as demonstrated by error messages of that nature)

What I would like to do is let the thread delivered by the 3rdPartyEventSource go back and have HandleNewEvent executed by the thread that created all of those instances (CreatorThread). Like:

    private void _3rdPartyEventSource_NewEvent(object o)
    {
        SomehowInvokeCreatorThread(new Action(() => HandleNewEvent(o)));
    }

    private void HandleNewEvent(object o)
    {
        InstanceOfControlClassA.doSomethingWith(o);
        InstanceOfControlClassB.doSomethingWith(o);
        InstanceOfControlClassC.doSomethingWith(o);  //which may access this
    }

How can I do that?

davecove
  • 1,001
  • 4
  • 16
  • 36
  • Can you do something like http://stackoverflow.com/questions/5868451/wpf-cross-thread-object-access ? – bmm6o Apr 18 '14 at 00:30
  • I am trying to do the 'chunky' thing in the last answer on that thread, but I don't know what 'r' is in my situation. – davecove Apr 18 '14 at 00:35

1 Answers1

1

There are many ways of doing this, but the following one might be the simplest. Just create your own wrapper event which will be fired on the UI thread:

public class ControlClassD
{
    public class WrapperEventArgs: EventArgs { public object Arg { get; set; } }
    public event EventHandler<WrapperEventArgs> WrapperEvent = delegate { };

    private 3rdPartyEventSource _3rdPartyEventSource = new 3rdPartyEventSource();

    public ControlClassD()
    {
        var dispatcher = Dispatcher.CurrentDispatcher;

        _3rdPartyEventSource.NewEvent += obj =>
              dispatcher.BeginInvoke(new Action(() => 
                  this.WrapperEvent(this, new WrapperEventArgs { Arg = obj })));

        this.WrapperEvent += ControlClassD_WrapperEventHandler;

        _3rdPartyEventSource.StartMakingEventsWhenSomethingHappens();
    }

    private void ControlClassD_WrapperEventHandler(
        object sender, WrapperEventArgs args)
    {
        InstanceOfControlClassA.doSomethingWith(args.Arg);
        InstanceOfControlClassB.doSomethingWith(args.Arg);
        InstanceOfControlClassC.doSomethingWith(args.Arg);
    }
}
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • Seems intriguing... definitely new to me. I think there are some mistakes tho? Can you check your example and my alterations? 1) I moved the definition of WrapperEventArgs outside of the definition for ClassD. 2) the definition of WrapperEventHandler doesn't seem valid inside or outside of ClassD, VS says it can't resolve that symbol. Where/how should WrapperEventHandler be defined? – davecove Apr 18 '14 at 03:25
  • @davecove, it's `EventHandler`, I typed out of head. – noseratio Apr 18 '14 at 03:31
  • 1
    @davecove, just keep in mind that `BeginInvoke` is asynchronous. That is, your event source may generate another `NewEvent`, while the previous `WrapperEvent` is still queued for processing on the UI thread. – noseratio Apr 18 '14 at 04:36