Subscribing an a method to and eventhandler with more parameter(s) - c#

I can't find solution similar to what I expect to happen. How can I add additional parameter to the method i want to subscribe to?
Suppose this line below is the code that subscribe to mouseover:
Panel cagePanel = new Panel();
cagePanel.MouseHover += new EventHandler(frmMain_MouseHover);
void frmMain_MouseHover(object sender, EventArgs e){
// I wanna add some 'int index' parameter after 'e' variable)
}
What I really want is just like this:
void frmMain_MouseHover(object sender, EventArgs e, int index){
strings[index] = "bla bla bla";
}

I can't find solution similar to what I expect to happen. How can I add additional parameter to the method i want to subscribe to?
You can't. How would the code raising the event (Panel.OnMouseHover or whatever) know what value to provide?
If you know at the time you subscribe the event, you can use a lambda expression to basically delegate the call, providing the extra information:
// 10 is just an example here - use whatever value you want for index
cagePanel.MouseHover += (sender, args) => frmMain_MouseHover(sender, args, 10);
EDIT: To iterate over each control in an array and subscribe an appropriate handler for each, you could use something like this:
for (int i = 0; i < array.Length; i++)
{
// See http://tinyurl.com/3b2hoft for reasons behind the copy
int index = i;
array[i].MouseHover += (s, args) => frmMain_MouseHover(s, args, index);
}
I would personally try to avoid needing this, however - can you not detect the index via the sender part, potentially using Array.IndexOf?

You can't, but if the extra data you need is related to what sent the event, you could use the sender value. For example, if you want to know the index in a list of controls of the item that raised the event, you could do something like:
int i = controlList.IndexOf(sender);

Related

Event Driven Programming

I've been reading this MSDN article and this question to try to understand events in .NET. Unfortunately, its not clicking for me and I'm having a lot of trouble. I'm trying to integrate this technique into my project, with little success.
Basically, I've got this class that will read numbers. Whenever it encounters a new number, I want it to fire an event called numberChanged.
So, I set up my event public event EventHandler numberChanged;. Later on, I fire my event when it encounters a number than isn't the same as the previous one.
if(currentNumber != previousNumber){
if(numberChanged != null){
numberChanged(this, new EventArgs());
}
}
But then I'm having trouble 'subscibing' to this event. If I do numberChanged += [something to do here] it errors saying that numberChanged is an event and not a type.
Is my explanation clear enough for some advice to be offered? Many thanks.
There are a number of ways to handle it, the most basic is to create a function:
public void MyNumberChangedHandler(object sender, EventArgs e)
{
//Your code goes here that gets called when the number changes
}
You then subscribe (one time only, usually in the constructor) by going:
numberChanged += MyNumberChangedHandler;
Or, you can use something called an anonymous (lambda) method, which is also assigned in your constructor (typically):
numberChanged += (sender, e) => {
//Your code here to handle the number changed event
};
To expand a little bit, care must be taken when using the lambda approach since you can create memory leaks and zombie objects. The .NET memory garbage collector is a mark-and-sweep system that removes objects when they are no longer in use. This post shows how hard it is to remove lambda event handlers: How to remove a lambda event handler .
Having an active event handler can keep your object alive even if it has been disposed! Here is an example of creating a zombie object (doesn't run in Fiddle but you can copy to your own console app) https://dotnetfiddle.net/EfNpZ5
Prints out:
I'm still alive
I'm still alive
I was disposed!
Press any key to quit
I'm still alive
I'm still alive
I'm still alive.
As everything else in the C# programming world, the events concept also follows specific rules and has it's own syntax. The wording is as follows:
an event defined as EventHandler is actually just a shortcut for a special method (delegate) signature - public delegate void EventHandler(object sender, EventArgs e)[1]. Whenever you have a signature in C# you always know what you need to write on the right sight or as a parameter, in order to connect/call some objects/methods/and so on.
after the event is defined, you need to subscribe in order to be informed whenever something happens. The syntax for subscribing an event is +=. Naturally for unsubscribing is -=. MSDN says that the syntax should be object.event += eventHandler (or object.event += new EventHandler(eventHandler);)
so after an event is defined (event Event SomeEvent;) all that left is to create a method that can be bound to this event. This method has to have the same signature as the EventHandler, so it should match the signature of [1] and can be something like private void numberChangedEventHandler(object sender, EventArgs eventArguments)
Now you know what you need to write on the right side of +=.
An example:
public class NumberSequence
{
// numbers to be compared
private readonly List<int> numbers = new List<int>();
// used to generate a random collection
private readonly Random random = new Random();
// tell me if the previous and next number are different
public event EventHandler DifferentNumbersEvent;
public NumberSequence()
{
// fill the list with random numbers
Enumerable.Range(1, 100).ToList().ForEach(number =>
{
numbers.Add(random.Next(1, 100));
});
}
public List<int> Numbers { get { return numbers; } }
public void TraverseList()
{
for (var i = 1; i < this.numbers.Count; i++)
{
if (this.numbers[i - 1] != this.numbers[i])
{
if (this.DifferentNumbersEvent != null)
{
// whoever listens - inform him
this.DifferentNumbersEvent(this, EventArgs.Empty);
}
}
}
}
}
Now before the class is used, define the event handler, that will listen and will be called, when the event is fired (wording again):
private void differentNumberEventHandler(Object sender, EventArgs eventArguments)
{
Console.WriteLine("Different numbers...");
}
And the usage:
var ns = new NumberSequence();
ns.DifferentNumbersEvent += differentNumberEventHandler;
ns.TraverseList();
Everything else is just syntactic sugar for this notation (lambda / anonymous methods / ...), for example:
object.Event += (s, e) => { // code ... }; is the same as object.Event += (Object sender, EventArgs eventArguments) => { // code ... };. Do you recognise the signature? - it is the same as the private void differentNumberEventHandler....
Often we need to pass information through the event, in this case maybe we want to see the two numbers. C# allows you to do this easily using custom event arguments. Just create a class that inherits the EventArgs class and add properties for the data that should be passed, in this case the numbers:
public class NumbersInfoEventArgs : EventArgs
{
public int Number1 { get; set; }
public int Number2 { get; set; }
}
And then specify, when declaring the event, that it will pass data of type NumbersInfoEventArgs (signatures again):
public event EventHandler<NumbersInfoEventArgs> DifferentNumbersEvent;
...
this.DifferentNumbersEvent(this, new NumbersInfoEventArgs
{
Number1 = this.numbers[i - 1],
Number2 = this.numbers[i]
});
And last but now least, the signature of the event handler should match the signature of the event:
private void differentNumberEventHandler(Object sender, NumbersInfoEventArgs eventArguments)
{
Console.WriteLine("Different numbers {0} - {1}", eventArguments.Number1, eventArguments.Number2);
}
And voila, the output is:
Different numbers 89 - 86
Different numbers 86 - 53
Different numbers 53 - 12
Different numbers 12 - 69
you can subscribe the event in this way:
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var num = new Number();
num.numberChanged +=(s,e) =>{
Console.WriteLine("Value was changed to {0}",num.Value); // in the demo below you can find another implementation for this sample using custom events
};
num.Value=10;
num.Value=100;
}
}
public class Number{
public event EventHandler numberChanged;
private int _value=0;
public int Value
{
get{
return _value;
}
set{
if(value!=_value){
_value=value;
if(numberChanged!=null)
numberChanged(this,null);
}
}
}
}
explanation:
since the EventHandler delegate has 2 parameters (sender, eventArgs) as mentioned here, you need to pass these params and I passed them as s and e
another way to subscribe this event like this:
var num = new Number();
num.numberChanged += NumberChanged_Event; // below is the delegate method
public void NumberChanged_Event(object sender,EventArgs e)
{
// your code goes here
}
I updated the demo to work with you own delegate to pass the old value and new value which can help in many cases.
here a working demo

Make buttons in List send unique data to method

I made a application which will place out buttons in a grid where the user specifies how big the playfield should be.
I create the buttons in a list, specify some data like backgroundimage, size, and location. I then need to, in some way make the different buttons execute different code. I figured I could do this in one method, (if there aren't any good ways to programmatically create methods), if I could somehow make the buttons send a unique piece of information to the method to identify which button is pressed.
public void buttonplacer()
{
int nbrofbtns = Form2.puzzlesize * Form2.puzzlesize;
List<Button> btnslist = new List<Button>();
for (int i = 0; i < nbrofbtns; i++)
{
Button newButton = new Button();
btnslist.Add(newButton);
this.Controls.Add(newButton);
newButton.Name = "btn" + i.ToString();
newButton.Width = btnsidelength;
newButton.Height = btnsidelength;
newButton.Top = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i / Form2.puzzlesize)));
newButton.Left = btnsidelength * Convert.ToInt32(Math.Floor(Convert.ToDouble(i)) - Math.Floor((Convert.ToDouble(i)) / (Form2.puzzlesize)) * (Form2.puzzlesize));
newButton.BackgroundImage = Lights_out_.Properties.Resources.LightsOutBlack;
newButton.Click += new EventHandler(Any_Button_Click);
}
}
void Any_Button_Click(object sender, EventArgs e)
{
}
(If you want to know I'm doing a game called "Light's out")
Thanks in advance!
The Any_Button_Click method receives an object sender that is the button that got clicked. You just need to cast it to a Button:
void Any_Button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
// do stuff here
}
You can use the button's Location property to figure out where it sits on the game board, or you can assign an arbitrary object to the button with any information you choose at initialization time using the Tag property like this:
button.Tag = "someHelpfulString";
or like this:
button.Tag = new Tuple<int, int>(xpos, ypos);
(where xpos and ypos are positions in the button grid)
or like this:
button.Tag = new ButtonInfoObject(foo, bar, baz);
(Here it's up to you to define the ButtonInfoObject class.)
As an alternative to other answers, and in particular to somewhat address the part "good ways to programmatically create methods", there is part of the C# language called Lambda Expressions. To keep long story short, you could write something along these lines:
newButton.Click += (s, e) =>
{
//here you have access to all variables accessible in current scope,
//including "newButton" and "i";
//you could, for example, call some method passing "i" as an argument
//or just put that method's code inside this block
};
The only downside of this approach is that you need to take some extra care if you're planning to unregister the handler at some later point (see this question or this question for reference).
EDIT
As pointed in comments I overlooked the fact that i stays in scope for the whole for loop, so using it inside lambda is pretty much pointless (all handlers will use it's final value). To make it behave like expected one can simply define a variable inside the loop so it goes out of scope at the end of each iteration and is stored separately for each handler:
var btnNo = i;
newButton.Click += (s, e) =>
{
//use "btnNo" instead of "i"
//you can still safely use "newButton" reference
//since it's defined inside the loop
}
Use the sender object to get the button's name that was pressed:
void Any_Button_Click(object sender, EventArgs e)
{
switch ((sender as Button).Name)
{
case "btn0":
//...
break;
case "btn1":
//...
break;
//...
}
}

How to properly send different parameters down for a standard Button Click

I know this has to have an easy answer, but I'm utterly failing to fathom the wealth of information on custom events, event handlers, and delegates. I have a custom messagebox class. I am trying to add the capability to do something based off of the state of a check box if the OK button is clicked. The buttons and the checkbox are added dynamically based upon input into a static Show method somewhat like the following:
if (!String.IsNullOrWhiteSpace(suicideCheckboxID))
{
suicideCheckBox = new CheckBox();
suicideCheckBox.AutoSize = true;
suicideCheckBox.Text = "Do not show this message again.";
suicideCheckBox.Location = new Point(xMargin, label.Bottom + yMargin);
suicideCheckBox.Checked = false;
suicideCheckBoxHeight = suicideCheckBox.Height;
form.Controls.Add(suicideCheckBox);
}
Button okButton = NewButton(DialogResult.OK, scaleFactor);
int x = (form.Width - okButton.Width) / 2;
okButton.Location = new Point(x, buttonYPosition);
form.Controls.Add(okButton);
form.AcceptButton = okButton;
form.CancelButton = okButton;
That's not the exact code, but it's fairly representative. My impulse is to use okButton.Clicked += new EventHandler(OKButton_clicked), but if I do that, the event generated only carries arguments for object sender and EventArgs e and I really need it to operate off of the state of the checkbox and an additional piece of text to indicate which messagebox is being shown so that the values can be stored in the registry.
My first attempt was to do something like okButton.Clicked += processSuicideCheckbox(suicideCheckboxID, suicideCheckBox);, but that seems to just process the contents and allow one to return an EventHandler that points to a method with the signature of object sender and EventArgs e. What am I missing here? What is the best way to pass in the arguments actually relevant to me?
You don't get to choose what is in the event handler for the Click event. Microsoft has already done that. You are stuck with the (object sender, EventArgs e) signature.
You do have a couple options:
Simply store the state in the class itself; the event handler will have access to it because it is inside the class.
Utilize a closure to do the same thing:
myButton.Click += (s, e) => ActualFunction(checkBox1.Checked);
Note that using the closure (via a lambda expression) is just hiding the details of maintaining this state (creating the class-level variables).

C# EventHandler with parameters changing its value

I have a table which keeps growing its size as the user inputs them, and each row has a 'X' Label to remove them as well.
table.RowCount is always up-to-date and so I have a function ~like following that is executed everytime I create a new row:
private void storeValues(){
Label removeLabel = new Label();
removeLabel.Text = "✗";
removLabel.Click += new EventHandler((s, e) => removeLabel_Click(s, e, table.RowCount));
}
The thing is that all removeLabel's always call the click event with the same parameter value which is the table.RowCount NOT the one I created them with but the value is currently having, so I'm always getting the last row deleted.
private void removeLabel_Click(object sender, EventArgs e, int index){
removeFromTable(index);
}
How can I save a fixed value to each removeLabel?
Yes; that is what happens with lexical closures. The only thing you are actually capturing here is the implicit this - the value of the expression this.table.RowCount is evaluated at the time the delegate is invoked, every time the delegate is invoked.
If you want to capture a snapshot: capture a snapshot
var count = table.RowCount;
removLabel.Click += new EventHandler((s, e) => removeLabel_Click(s, e, count));
removeLabel.Tag = table.RowCount;
then get the Tag value in the event handler

Pass an event as a parameter to a method [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to pass an event to a method?
Is it possible to pass an event as a parameter to a method?
For example, the following method subscribes to the event, does work, and unsubscribes from the event:
void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
IEnumerable<TElement> elements,
??? elementEvent)
where TEventArgs: EventArgs
{
EventHandler<TEventArgs> handler = (sender, e) => { /* Handle an event */ };
foreach (var element in elements)
{
// Subscribe somehow
element.elementEvent += handler
}
// Do things
foreach (var element in elements)
{
// Unsubscribe somehow
element.elementEvent -= handler
}
}
Client code:
var elements = new [] { new Button(), new Button() };
SubscribeDoAndUnsubscribe(elements, ??? /* e => e.Click */);
If it's not possible, how do I achieve the similar logic in other ways? Shall I pass pair of delegates for subscribe/unsubscribe methods?
You have in fact discovered that events are not "first class" in C#; you cannot pass around an event as data. You can pass around a delegate to a method associated with a receiver as a first-class object by making a delegate. You can pass around a reference to any variable as a (mostly) first-class object. (I say "mostly" because references to variables cannot be stored in fields, stored in arrays, and so on; they are highly restricted compared to other kinds of data.) You can pass around a type by obtaining its Type object and passing that around.
But there is no way to directly pass around as data an event, property, indexer, constructor or destructor associated with a particular instance. The best you can do is to make a delegate (or pair of delegates) out of a lambda, as you suggest. Or, obtain the reflection object associated with the event and pass that around, along with the instance.
No, unfortunately not.
If you look at Reactive Extensions, that suffers from a similar problem. Three options they use (IIRC - it's been a while since I've looked):
Pass in the corresponding EventInfo and call it with reflection
Pass in the name of the event (and the target if necessary) and call it with reflection
Pass in delegates for subscription and unsubscription
The call in the latter case would be something like:
SubscribeAndDoUnsubscribe(elements,
handler => e.Click += handler,
handler => e.Click -= handler);
and the declaration would be:
void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
IEnumerable<TElement> elements,
Action<EventHandler<TEventArgs>> subscription,
Action<EventHandler<TEventArgs>> unsubscription)
where TEventArgs: EventArgs
You're trying to get around type safety, and you can't do so without using reflection. I'll show you an even simpler example of what you're trying to do.
void DoSomethingOnSomethingElse(T obj, Action method)
{
obj.method();
}
C# doesn't work this way. How does the compiler know that all Ts have the method method? It doesn't, and can't. Similarly, not every TElement in your code will have an event Click for example.
It sounds like you just want to set a single use event handler on a set of objects. You can do this quite easily...
EventHandler handler = null;
handler = (s,e) =>
{
DoSomething(e);
var b = (Button) s;
b.Click -= handler;
}
foreach (var button in buttons)
{
button.Click += handler;
}
This, obviously, only works with buttons, but as I write this, I see Jon Skeet has shown you a more general solution, so I'll end here.

Categories

Resources