Ryan Peschel
Ryan Peschel

Reputation: 11756

How to pass an event to a method and then subscribe to it?

Event Handler

public void DeliverEvent(object sender, EventArgs e)
{

}

#1: This Works

public void StartListening(Button source)
{
    source.Click += DeliverEvent;
}

#2: And so does this..

public void StartListening(EventHandler eventHandler)
{
    eventHandler += DeliverEvent;
}

But in #2, you cannot call the method because if you try something like this:

StartListening(button.Click);

You get this error:

The event 'System.Windows.Forms.Control.Click' can only appear on the left hand side of += or -=

Is there any way around that error? I want to be able to pass the event and not the object housing the event to the StartListening method.

Upvotes: 3

Views: 3494

Answers (4)

supercat
supercat

Reputation: 81159

Unfortunately, although events and properties in .net exist in the type system, and while it would have been both possible and useful for Microsoft to make them exist as concrete types (each instance would contain a pair of delegates, either for add/remove or for get/set), Microsoft has not created any such concrete types nor does it seem likely to do so. Consequently, there is no remotely-nice-looking way to pass a property or event to a routine. Instead, for events, one would either have to pass a pair of delegates--one for an Add method and one for a Remove method, or else possibly pass a class or struct which holds the two delegates in question, or else pass the object whose events should be used along with the name of the event, and have the called routine use Reflection to locate the add/remove handlers associated with the named event.

Upvotes: 0

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236218

You can't pass event as parameter, because it's not an object of delegate type. Event is a pair of two methods - add and remove.

I.e. Click event of button class actually looks like

public void add_Click(EventHandler value) 
{
   // combine delegate
}

public void remove_Click(EventHandler value) 
{
   // remove delegate
}

If you want to subscribe to event, you should pass whole object and then call add method for that particular event (use += syntax).

Upvotes: 1

Enigmativity
Enigmativity

Reputation: 117064

The Microsoft Reactive Extensions or Rx framework provides a method to turn an event into an observable. Events cannot be passed around as parameters, but observables can. When observables are subscribed to they set up handlers for the underlying event.

The signature (of one of the overloads) looks like this:

IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(
        Action<TDelegate> addHandler, Action<TDelegate> removeHandler
    ) where TEventArgs: EventArgs

For example, to define a MouseMove observable from an event this code can be used:

        IObservable<EventPattern<MouseEventArgs>> mouseMoves =
            Observable
                .FromEventPattern<MouseEventHandler, MouseEventArgs>(
                    h => richTextBox1.MouseMove += h,
                    h => richTextBox1.MouseMove -= h);

You can then pass around a reference to IObservable<EventPattern<MouseEventArgs>> mouseMoves and subscribe to it like so:

        var subscription = 
            mouseMoves
                .Subscribe(ep =>
                {
                    var x = ep.EventArgs.X;
                    var y = ep.EventArgs.Y;
                    // etc
                });

Detaching from the observable/event becomes as simple as calling this:

        subscription.Dispose();

You can get Rx via Nuget.

Upvotes: 1

Benjamin Gale
Benjamin Gale

Reputation: 13177

You are mixing events with delegates. You should probably read this article by Jon Skeet which will explain the differences between the two.

The reason the second option doesn't work is because the method expects a parameter that is a delegate that conforms to the EventHandler signature.Button.Click refers to an event rather than a delegate. Events just encapsulate delegates.

I'm not sure what you are trying can be done. Conceptually what you want to do doesn't actually make sense; it is the logic of the handler you want to pass around not the event itself.

Take a look at this post which looks at some ways of simulating the effect you want.

Upvotes: 1

Related Questions