Custom event args on a dynamically assigned event handler - c#

I am assigning a click event handler like so
var buttonEvent = button.GetType().GetEvent("Click");
var eventMethod = GetType().GetMethod("button_Click");
var handler = Delegate.CreateDelegate(buttonEvent.EventHandlerType, this, eventMethod);
buttonEvent.AddEventHandler(button, handler);
The click event handler is like so:
void button_Click(object sender, EventArgs e) { }
How can I pass custom args into the handler? Ideally I would love
void button_Click(object sender, MyCustomEventArgs e) { }
where I can set MyCustomEventArgs when I assign the event.

Plain and simple, you can't.
The Button class invokes that event, and when it invokes it, it populates the arguments. In this case, with an EventArgs object (no custom data allowed).
This holds true for any event. Unless the class raising the event supports some way of inserting custom data into the event stream, you can't control what it gives you via the event handler.
Note that there is one clever trick for UI event handlers. Because the sending object is held in "sender" and you can put anything in the "Tag" property, you can have custom data in your event by doing:
control.Tag = someObject;
control.Click += (o, e) =>
{
Control c = o as Control;
MyObject data = c.Tag as MyObject;
//use data
};
Also, in WPF, you can do this with CommandParameter since that is also a generic object. Of course, thats not event handlers, but its still the same general idea.

Related

How can I add an additional parameter to a button click EventHandler?

In C#, how is the best way to add an additional property to a button event call?
Here is the code for the EventHandler:
button.Click += new EventHandler(button_Click);
Here is the code for the button_Click:
private void button_Click(object sender, EventArgs e)
{
}
If I want to add a PropertyGrid parameter to the button_Click function parameters, how is the best way to do this?
I am wanting to do this as the button.Click code is in a function that has a PropertyGrid parameter, and in the button_Click function, I need to set the PropertyGrid selected object. This is only set when the button.Click button is clicked.
If I set the tag of the button to be a PropertyGrid object, how can I retrieve this tag object in the button_Click code?
The button_Click event is called from an object, and the sender is the object, that is not the button.
Can I please have some help with the code?
You cannot convince a Button that it should know anything about a PropertyGrid. When it fires its Click event then it can only tell you about what it knows. Which is cast in stone.
You trivially work around this by using a lambda expression, it can capture the PropertyGrid argument value and pass it on to the method. Roughly:
private void SubscribeClick(PropertyGrid grid) {
button.Click += new EventHandler(
(sender, e) => button_Click(sender, e, grid)
);
}

How to sync up to different events in Winforms

If I have a button which does something and also a double-click event on a data grid which I want to do the same thing, what is the best way to ensure that only one function has to be maintained?
Apart from doing the following, is there any fancy C# way to indicate that two events are to do the same thing?
void button1_Click(...) { MyFunction(); }
void dataGrid1_DoubleClick(...) { MyFunction(); }
void MyFunction() { // do stuff }
I suppose that you are talking about a DataGridView (WinForms) so the signature of the event DoubleClick in the DataGridView and the signature of Click event on a button control is the same.
(An EventHadler). In this case you can simply set the same method using the form designer or manually bind the event
dataGridView1.DoubleClick += new EventHandler(MyFunction);
button1.Click += new EventHandler(MyFunction);
Of course the MyFunction method should match the expected signature of an EventHandler
private void MyFunction(object sender, EventArgs e)
{
// do your work
}
Reviewing my answer after a few minutes I wish to add:
If you find yourself in a situation in which you need to differentiate between the controls using the sender object (like Control c = sender as Control; if (c.Name == "someName") ) I really suggest you to return to the first idea. Call a common method but keep the EventHandler separated for each control involved.
Using VS, in the form's designer view You can set the procedure You want to call to each control's each event in the control's properties window.
image
Just to add to what Steve said, you will want to bind these events to your function manually in the Load event of your form, instead of using the events under the lightning bolt in the properties window in the designer, like so:
private void Form1_Load(object sender, EventArgs e)
{
button1.Click += MyMethod;
dataGridView1.DoubleClick += MyMethod;
}
void MyMethod(object sender, EventArgs e)
{
//Do Stuff
}
Also, declaring a new instance of the EventHandler class has been redundant since Anonymous methods were introduced to C#, you can just point the event directly at the method as shown above.

Trigger control's event programmatically

Assume that I have a WinFoms project. There is just one button (e.g. button1).
The question is: is it possible to trigger the ButtonClicked event via code without really clicking it?
Button controls have a PerformClick() method that you can call.
button1.PerformClick();
The .NET framework uses a pattern where for every event X there is a method protected void OnX(EventArgs e) {} that raises event X. See this Msdn article. To raise an event from outside the declaring class you will have to derive the class and add a public wrapper method. In the case of Button it would look like this:
class MyButton : System.Windows.Forms.Button
{
public void ProgrammaticClick(EventArgs e)
{
base.OnClick(e);
}
}
You can just call the event handler function directly and specify null for the sender and EventArgs.Empty for the arguments.
void ButtonClicked(object sender, EventArgs e)
{
// do stuff
}
// Somewhere else in your code:
button1.Click += new EventHandler(ButtonClicked);
// call the event handler directly:
ButtonClicked(button1, EventArgs.Empty);
Or, rather, you'd move the logic out of the ButtonClicked event into its own function, and then your event handler and the other code you have would in turn call the new function.
void StuffThatHappensOnButtonClick()
{
// do stuff
}
void ButtonClicked(object sender, EventArgs e)
{
StuffThatHappensOnButtonClick();
}
// Somewhere else in your code:
button1.Click += new EventHandler(ButtonClicked);
// Simulate the button click:
StuffThatHappensOnButtonClick();
The latter method has the advantage of letting you separate your business and UI logic. You really should never have any business logic in your control event handlers.
Yes, just call the method the way you would call any other. For example:
private void btnSayHello_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello World!");
}
private void btnTriggerHello_Click(object sender, EventArgs e)
{
btnSayHello_Click(null, null);
}
button1.PerformClick();
But if you have to do something like this maybe it's better to move the code you have under the event on a new method ?
Why don't you just put your event code into a Method. Then have the Event execute the method. This way if you need to execute the same code that the Event rises, you can, but simply just calling the "Method".
void Event_Method()
{
//Put Event code here.
MessageBox.Show("Hello!");
}
void _btnSend_Click(object sender, EventArgs e)
{
Event_Method();
}
void AnotherMethod()
{
Event_Method();
}
Make sense? Now the "Click" event AND anywhere in code you can trigger the same code as the "Click" event.
Don't trigger the event, call the method that the event calls. ;)
In most cases you would not need to do that. Simply wrap your functionality in functions related to a specific purpose (task). You call this function inside your event and anywhere else it's needed.
Overthink your approach.
I recently had this problem where I wanted to programatically click a button that had multiple event handlers assigned to it (think UserControl or derived classes).
For example:
myButton.Click += ButtonClicked1
myButton.Click += ButtonClicked2;
void ButtonClicked1(object sender, EventArgs e)
{
Console.WriteLine("ButtonClicked1");
}
void ButtonClicked2(object sender, EventArgs e)
{
Console.WriteLine("ButtonClicked1");
}
When you click the button, both functions will get called. In the instances where you want to programmatically fire an event handler for a function from a form (for example, when a user presses enter in a Text field then call the InvokeOnClick method passing through the control you. For example
this.InvokeOnClick(myButton, EventArgs.Empty);
Where this is the Form instance you are in.
use a for loop to call the button_click event
private void btnadd_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i <= 2; i++)
StuffThatHappensOnButtonClick();
}
void StuffThatHappensOnButtonClick()
{
........do stuff
}
we assume at least one time you need click the button

passing Event Arguments of original handler to Routed Event in wpf

The following code shows a normal event and a routed event. Here I have used the same event name for explaining purposes but in reality I am using just the routed event.
//Normal Event
public event SelectedHandler Selected;
public delegate void SelectedHandler(Object Sender, RoutedEventArgs e);
//Routed Event
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent(
"Selected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MyUserControl));
//add remove handlers
public event RoutedEventHandler Selected
{
add { AddHandler(SelectedEvent, value); }
remove { RemoveHandler(SelectedEvent, value); }
}
I am raising these events from a couple of event handlers as follows
private void lstvMyView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Normal Event Raise
if (Selected != null)
Selected(this, e);
//Routed Event Raise
RoutedEventArgs args = new RoutedEventArgs(SelectedEvent);
RaiseEvent(args);
}
private void lstvMyView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//Normal Event Raise
if (Selected != null)
Selected(this, e);
//Routed Event Raise
RoutedEventArgs args = new RoutedEventArgs(SelectedEvent);
RaiseEvent(args);
}
When I am handling the normal Event I am able to send the args of both the handlers to the event but in Routed Event the args will be a new instance. I want to pass the args of both the handlers to the Routed Event. Is it possible to achieve this? If yes then how?
first of all you do not need this (and should remove it):
//Normal Event
public event SelectedHandler Selected;
public delegate void SelectedHandler(Object Sender, RoutedEventArgs e);
i.e. you do not need to define a separate "normal" event, because you have already done it with this declaration:
public event RoutedEventHandler Selected
{
add { AddHandler(SelectedEvent, value); }
remove { RemoveHandler(SelectedEvent, value); }
}
with the above code block you are "wrapping" the routed event with a "normal" (clr) one so the users of your class can use it with the "normal" syntax (i.e. instanceOfMyUserControl.Selected += ....)
second, if you want the event arguments of your routed event to be the same as the ones of the SelectionChanged event of the the ListView you are listening to, you should declare your routed event this way:
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent(
"Selected", RoutingStrategy.Bubble,
typeof(SelectionChangedEventHandler),
typeof(MyUserControl));
//add remove handlers
public event SelectionChangedEventHandler Selected
{
add { AddHandler(SelectedEvent, value); }
remove { RemoveHandler(SelectedEvent, value); }
}
Notice that I have substituted the RoutedEventHandler with SelectionChangedEventHandler, as it is the predefined one that can "carry" SelectionChangedEventArgs.
Now for the rising of the event.
You do not need to rise both the "normal" and the routed one (as the "normal" is a wrapper for the routed), so you should delete this:
//Normal Event Raise
if (Selected != null)
Selected(this, e);
and rise only the routed version, which can be done this way:
SelectionChangedEventArgs args =
new SelectionChangedEventArgs(SelectedEvent, e.RemovedItems, e.AddedItems);
RaiseEvent(args);
Notice that I am using the event arguments from the original event to set the AddedItems and RemovedItems of the one custom one.
To follow on your last comment (can one give a link to a particular comment ?) I will write another answer so people can follow.
I think you are missing the point of events altogether. What do you want the handler of your event to know when it receives it? That is generally the purpose of the event arguments - you pass some info to the handler with which you give some background on what exactly happened.
So the first time you are raising your event, you will do it this way:
private void lstvMyView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectionChangedEventArgs args =
new SelectionChangedEventArgs(SelectedEvent, e.RemovedItems, e.AddedItems);
RaiseEvent(args);
}
You first have to construct the arguments and then use the RaiseEvent() function with that arguments, because you want to raise a wpf's special routed type of event. The arguments have to be an instance of a class that inherits RoutedEventArgs. Notice that you are constructing SelectionChangedEventArgs which are defined in wpf to "carry" additional information to the handler of the event - namely which items have been removed from the selection and which have been added, so when the handler receives the event it can use that info if he wants.
About this think you are doing:
//Normal Event Raise
if (Selected != null)
Selected(this, e);
basically - (as I said in my first answer) remove it, you do not need it.
So what is bothering you is the second time you are raising the event. This is a prototype of what you have to do:
private void lstvMyView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
SelectionChangedEventArgs args =
new SelectionChangedEventArgs(SelectedEvent, ?, ?);
RaiseEvent(args);
}
as you see, again you have to construct the SelectionChangedEventArgs and use the RaiseEvent() function to raise the event. But this time you can not use e.RemovedItems and e.AddedItems, because you are handling a MouseLeftButtonUp event, that (via its args) carries information about the mouse state - not what items have been added to the selection (after all its a mouse event, not a selection event).
You have to decide with what to replace the two question marks - as I said in the comments to the first answer, you have to decide what information you want to convey to the user of your event. What means that the a mouse button is no longer down on the "lstvMyView"? The simplest thing to do is this:
SelectionChangedEventArgs args =
new SelectionChangedEventArgs(SelectedEvent, Enumerable.Empty<object>(), Enumerable.Empty<object>());
RaiseEvent(args);
with which you are raising the event and telling its consumer that no items have been removed from the selection and no items have been added.

C# event and delegate

I want to detach the custom event but could not detach. Below I am using -= to detach the event. I assume after this, the TextChanged2 method should not be invoked as I have unregistered the event. Is my understanding wrong?
public delegate void TextChangedEventHandler1(object sender, TextBoxargs ta);
public event TextChangedEventHandler1 TextChanged1;
private void textBox1_TextChanged(object sender, EventArgs e)
{
this.TextChanged1 -= new TextChangedEventHandler1(TextChanged2);
TextChanged2(sender, e);
}
public void TextChanged2(object sender, EventArgs e)
{
textBox1.Text = textBox1.Text.ToUpper();
}
What you are doing is right. But using the following line of the code you can detach the event handler.
this.TextChanged1 -= new TextChangedEventHandler1(TextChanged2);
But on the second line you called the function directly so that it called the textchange2 function:
TextChanged2(sender, e);
I want to detach the custom event but
could not detach.
You do. You detach very well your event.
TextChanged2 method should not be
invoked as I have unregistered the
event.
It should not be invoked when this.textChanged1, but you invoke it yourself by calling TextChanged2(sender, e);
I suggest you give some more reasonable names to your methods, controls, and events. I could imagine half the confusion here stems from confusing names.
For example, in one comment to an answer, you mention that if you don't call the TextChanged2 event handler (for the TextChanged1 event...) explicitly, it will never get called. This would lead to the question when, and where, you raise the TextChanged1 event. If you have indeed subscribed the TextChanged2 handler to the TextChanged1 event with the += operator, then the handler will be invoked as soon as the event is raised.
Use
this.TextChanged1 -= TextChanged2;

Categories