68

Let's say I want to pass some extra data when assigning an event handler. Consider the following code:

private void setup(string someData)
{
     Object.assignHandler(evHandler);
}

public void evHandler(Object sender)
{
    // need someData here!!!
}

How would I go about getting someData into my evHandler method?

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Andy Hin
  • 30,345
  • 42
  • 99
  • 142
  • I have a more standard solution in this question, first answer http://stackoverflow.com/questions/14058412/passing-parameter-to-an-event-handler/14058441#14058441 – Saw Nov 25 '13 at 14:09

10 Answers10

56
private void setup(string someData)
{
     Object.assignHandler((sender) => evHandler(sender,someData));
}
public void evHandler(Object sender, string someData)
{
    // need someData here!!!
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • 2
    This is a great solution, but what if the EventHandler has TArgs already. Such as += new EventHandler(evHandler)? – Roast Dec 23 '11 at 13:58
  • Hi @Lily , it's not entirely clear what you are asking. It might be better to post a question (rather than a comment) with a bit more detail and some code to demonstrate what you're trying to do. – spender Dec 24 '11 at 02:34
  • 1
    Thanks, this solved a problem I was having, but I'm new to C# so would you mind explaining what this language construct is or giving me a keyword to google for an explanation? – Tom Smilack Apr 11 '12 at 16:44
  • @TomSmilack, take a look at lambda expressions and capturing variables with closures. Basically, lambda expressions are a shorthand way of declaring a method (...and so much more, but that's a different story) – spender Apr 11 '12 at 16:56
  • I have a more standard solution in this question, first answer: http://stackoverflow.com/questions/14058412/passing-parameter-to-an-event-handler/14058441#14058441 – Saw Nov 25 '13 at 14:07
  • Hmmm, I see, then the problem here is to mark the other question as a duplicate to this question, but your answer is very good for this very specific situation, I've read the answer very quickly but haven't seen the event args class, however thank you for response and I will upvote the question whenever I can :) – Saw Nov 26 '13 at 08:06
  • cn.InfoMessage += ((s, e) => { Console.WriteLine("[" + DBName + "] [Backup] " + e.Message); }); This is probably what most people are looking for. Where DBName is a local variable, which gets in the scope. – Shiroy Aug 23 '17 at 22:09
  • This is only useful if you never want to unsubscribe :( – Clonkex Jul 21 '22 at 21:49
35

I had a hard time figuring out @spender's example above especially with: Object.assignHandler((sender) => evHandler(sender,someData)); because there's no such thing as Object.assignHandler in the literal sense. So I did a little more Googling and found this example. The answer by Peter Duniho was the one that clicked in my head (this is not my work):

snip

The usual approach is to use an anonymous method with an event handler that has your modified signature. For example:

void Onbutton_click(object sender, EventArgs e, int i) { ... }

button.Click += delegate(object sender, EventArgs e) 
{ Onbutton_click(sender, e, 172); };

Of course, you don't have to pass in 172, or even make the third parameter an int. :)

/snip

Using that example I was able to pass in two custom ComboBoxItem objects to a Timer.Elapsed event using lambda notation:

simulatorTimer.Elapsed +=
(sender, e) => onTimedEvent(sender, e,
(ComboBoxItem) cbPressureSetting.SelectedItem,
(ComboBoxItem) cbTemperatureSetting.SelectedItem);

and then into it's handler:

static void onTimedEvent(object sender, EventArgs e, ComboBoxItem pressure, ComboBoxItem temperature)
    {
        Console.WriteLine("Requested pressure: {0} PSIA\nRequested temperature: {1}° C", pressure, temperature);
    }

This isn't any new code from the examples above, but it does demonstrate how to interpret them. Hopefully someone like me finds it instructive & useful so they don't spend hours trying to understand the concept like I did.

This code works in my project (except for a non-thread-safe exception with the ComboBoxItem objects that I don't believe changes how the example works). I'm figuring that out now.

delliottg
  • 3,950
  • 3
  • 38
  • 52
  • 2
    The only problem with this is that if you want to unsubscribe the event (and most likely you should), you'd be unsubscribing via an anonymous delegate, which you should not be doing - see [this answer to a related question](https://stackoverflow.com/a/41199095/422845). – jbyrd Mar 29 '18 at 20:43
19

Captured variables:

private void setup(string someData)
{
    Object.assignHandler((sender,args) => {
        evHandler(sender, someData);
    });
}

public void evHandler(Object sender, string someData)
{
    // use someData here
}

Or (C# 2.0 alternative):

    Object.assignHandler((EventHandler)delegate(object sender,EventArgs args) {
        evHandler(sender, someData);
    });
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

you can try doing this:

string yourObject;

theClassWithTheEvent.myEvent += (sender, model) =>
{
 yourObject = "somthing";
}
Idan
  • 509
  • 2
  • 10
  • 24
1

My question that was similar was marked a duplicate so thought I'd add an answer here since it won't let me on my question.

class Program
    {
        delegate void ComponentEventHandler(params dynamic[] args);

        event ComponentEventHandler onTest;

        static void Main(string[] args)
        {  
            Program prg = new Program();

            // can be bound to event and called that way
            prg.onTest += prg.Test;
            prg.onTest.Invoke("What", 5, 12.0);

            Console.ReadKey();
        }

        public void Test(params dynamic[] values)
        {
            // assign our params to variables
            string name = values[0];
            int age = values[1];
            double value = values[2];

            Console.WriteLine(name);
            Console.WriteLine(age);
            Console.WriteLine(value);
        }
    }
user441521
  • 6,942
  • 23
  • 88
  • 160
0

You could create a custom object having additional properties based on Object:

class CustomObject : Object
{
    public string SomeData;
}

private void setup(string someData)
{
    CustomObject customObject = new CustomObject { SomeData = someData };
    CustomObject.assignHandler(evHandler);
}

public void evHandler(Object sender)
{
    string someData = ((CustomObject)sender).SomeData;
}

If the data should not be changed anymore after initialization, you could also add a custom constructor, for example.

Michael Geier
  • 1,653
  • 1
  • 16
  • 18
0

Here is my one-line solution that pass extra parameters to a timer handler.

private void OnFailed(uint errorCode, string message)
{
    ThreadPoolTimer.CreateTimer((timer) => {
    UI.ErrorMessage = string.Format("Error: 0x{0:X} {1}", errorCode, message);
    }, System.TimeSpan.FromMilliseconds(100));
}
Ramius
  • 121
  • 8
0

Well, the simplest method id to make someData a member variable like so:

public class MyClass
{
    private string _eventData;

    private void setup(string someData) 
    {
       _eventData = someData;
       Object.assignHandler(evHandler);
    }

    public void evHandler()
    {
        // do something with _eventData here
    }
}

I'm not sure that's the best way to do it, but it really depends on the event type, the object, etc.

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
0

This solution offers a way to pass extra parameters to an event handler while still allowing to unsubscibe:

Within the Subscribe() function of my example I create an Action that invokes a lambda function that supplies my event handler with the event args and my extra parameter. I then store this Action in a dictionary. When I want to unsubscribe, I can use the stored Actions to do so.

This works, I read the length of listeners before and after unsubscribing and it did decrease - you can unsubscribe again without problems.

    public class Player
    {
        public Action<JumpInfo> OnJump;
    }

    public class PlayerJumpListener
    {
        public List<Player> MyPlayerList;
        private Dictionary<Player, Action<JumpInfo>> _jumpActionsByPlayer = new Dictionary<Player, Action<JumpInfo>>();

        private void Subscribe()
        {
            foreach (Player player in MyPlayerList)
            {
                Action<JumpInfo> playerJumpAction = (jumpInfo) => HandlePlayerJump(jumpInfo, player);
                player.OnJump += playerJumpAction;

                _jumpActionsByPlayer.Add(player, playerJumpAction);
            }
        }

        private void Unsubscibe()
        {
            foreach (KeyValuePair<Player, Action<JumpInfo>> kvp in _jumpActionsByPlayer)
            {
                kvp.Key.OnJump -= kvp.Value;
            }
        }

        private void HandlePlayerJump(JumpInfo jumpInfo, Player player)
        {
            // player jumped
        }
    }
Florian Wolf
  • 171
  • 7
0

I scoured the internet before a coworker kindly helped me, and boy I felt dumb. Brackets is the solution for the EventHandler.

Ex.

event EventHandler<(int, bool)> EventName;

and then pick it up with:

private void Delegate_EventName(object sender, (int, bool) e)

you can then access the info:

var temp = e.Item1;<br>
var temp2 = e.Item2;<br>

or you can add names as you would expect for parameters and call them via e:

private void Delegate_EventName(object sender, (int num, bool val) e)

you can then access the info:

var temp = e.num;
var temp2 = e.val;
CinCout
  • 9,486
  • 12
  • 49
  • 67
ZadiusC
  • 1
  • 1