How to define an extra parameter in a ClickHandler? [duplicate] - c#

I've been looking for an answer for about an hour on Google but I did not found exactly what I'm looking for.
Basically, I have a static Helper class that helps perform many things I do frequently in my App. In this case, I have a method named "CreateDataContextMenu" that creates a context menu on a given TreeView control.
public static void CreateDataContextMenu(Form parent, TreeView owner, string dataType)
{ ... }
TreeView owner is the control in which I will associate my context menu.
Then later on I add a Click event to a MenuItem like this:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click);
The problem I have here is that I want to pass "owner" and "dataType" as arguments to the menuItemFolder_Click event.
I tried the following:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
(...)
private static void menuItemFolder_Click(object sender, System.EventArgs e, Treeview owner, string dataType)
{...}
But it doesn't work at all. It might be very naive of me to do it that way but I"m not very comfortable with event handler yet.
Any idea on how I could do that?
My first guess is that I need to create my own EventHandler for this specific case. Am I going in the right direction with that?

You should create a lambda expression that calls a method with the extra parameters:
menuItemFolder.Click += (sender, e) => YourMethod(owner, dataType);

Honest admission up front: I have not tried the code below.
I think the reason
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
won't work is because you are actually passing to System.EventHandler () the result of the invocation of menuItemFolder_Click () with the parameters provided. You are not passing a pointer to the function itself.
Try to write another function that implements the details of menuItemFolder_Click (). See if something like
private void menuItemFolder_Click_Helper (object sender, EventArgs e, object Owner, object DataType) {
// implement details here
}
and then call the function from within menuItemFolder_Click ().

I think the simplest code would be this:
EventHandler myEvent = (sender, e) => MyMethod(myParameter);//my delegate
myButton.Click += myEvent;//suscribe
private void MyMethod(MyParameterType myParameter)
{
//Do something
//if only one time
myButton.Click -= myEvent;//unsuscribe
}

Passing custom args into an event handler is not too difficult. Below is a clean and easily reusable method of doing so. Check it:
public class MyClass
{
public CustomArgsEventHandler MyEvent1;
public MyClass(){MyEvent1+=observer;}
public void observer(object sender, CustomEventArgs e){print(e.myArg);}
//...
}
//place in the same file if you like!
public class CustomEventArgs : EventArgs
{
public float myArg {get;set;}
public CustomEventArgs (float d) { myArg = d; }
}
public delegate void CustomArgsEventHandler (object sender, CustomEventArgs e);

Related

Setting up events for separate classes

I have a class Character, that stores 3 CharacterAttribute classes. What I want is that at a certain point Character would do something if an event is raised within one of the CharacterAttribute classes.
class Character
{
public CharacterAttribute Physical;
public Character Create(...)
{
Physical = new CharacterAttribute();
Physical.SkillPointGained += this.OnSkillPointGained();
// ERROR: cannot convert void to System.EventHandler
}
public void OnSkillPointGained()
{
// do stuff here after the event has been fired
}
}
class CharacterAttribute
{
public event EventHandler SkillPointGained;
public void GainExperience(...)
{
SkillPointGained(this, new EventArgs()); // would raise the event here
}
}
I get a "cannot implicitly convert void to System.EventHandler" error. What am I missing?
Two lines of code need to be fixed up a little bit.
public void OnSkillPointGained()
should read:
void OnSkillPointGained(object sender, EventArgs e)
And
Physical.SkillPointGained += this.OnSkillPointGained();
Should read:
Physical.SkillPointGained += this.OnSkillPointGained;
(...however I like the elegance of Marc's answer better.)
EventHandler has the delegate signature of void EventHandler(object sender, EventArgs args). You are a: matching the wrong signature, and b: you're invoking the method rather than targeting the method as a delegate.
You can cheat, though:
Physical.SkillPointGained += delegate { this.OnSkillPointGained(); };
This creates an anonymous method that acts as the immediate handler, drops the sender/args, and invokes your OnSkillPointGained method.

How do I pass objects in EventArgs

I have a usercontrol that raises an event after communicating with a web service. The parent handles this event when raised. What I thought would be the proper approach would be to pass the object returned from the webservice to the parent as eventargs???
If this is the proper way I can't seem to find the instructions on how to do so.
UserControl
public event EventHandler LoginCompleted;
then later after the service returns biz object:
if (this.LoginCompleted != null)
{
this.LoginCompleted(this, new EventArgs() //this is where I would attach / pass my biz object no?);
}
Parent
ctrl_Login.LoginCompleted += ctrl_Login_LoginCompleted;
....snip....
void ctrl_Login_LoginCompleted(object sender, EventArgs e)
{
//get my object returned by login
}
So my question is what would be the "approved" method for getting the user object back to the parent? Create a property class that everything can access and put it there?
You would have to declare your event using EventHandler<T> where T is your class that derives from EventArgs:
public event EventHandler<LoginCompletedEventArgs> LoginCompleted;
LoginCompletedEventArgs could look like this:
public class LoginCompletedEventArgs : EventArgs
{
private readonly YourBusinessObject _businessObject;
public LoginCompletedEventArgs(YourBusinessObject businessObject)
{
_businessObject = businessObject;
}
public YourBusinessObject BusinessObject
{
get { return _businessObject; }
}
}
Usage would be like this:
private void RaiseLoginCompleted(YourBusinessObject businessObject)
{
var handler = LoginCompleted;
if(handler == null)
return;
handler(this, new LoginCompletedEventArgs(businessObject));
}
Please notice how I implemented RaiseLoginCompleted. This is a thread-safe version of raising the event. I eliminates a possible NullReferenceException that can occur in a race condition scenario where one thread wants to raise the event and another thread un-subscribes the last handler after the if check but before actually invoking the handler.
Well, you could do all that or you could define a delegate as your EventHandler and define your properties in its signature.
Such as:
public delegate void MyEventEventHandler(int prop1, string prop2, object prop3...);
public event MyEventEventHandler MyEvent;
I recommend use named tuples with EventHandler<TEventArgs>.
I like olddog's answer. Microsoft already has this delegate EventHandler< TEventArgs >.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
You don't need to inherits from EventArgs.
Declare your event handler with named tuples.
public event EventHandler<(int id, string message, object LoginObject)> LoginCompleted;
In your client code, assign method to the LoginCompleted event handler
option 1: use lambda
LoginCompleted += (o, e) =>
{
Console.WriteLine($"Hello, sender is {o.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
};
option 2: call a local method
LoginCompleted += onLoginCompleted;
private static void onLoginCompleted (object sender, (int id, string message, object LoginObject) e)
{
Console.WriteLine($"Hello, sender is {sender.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
}
I just wrote an example, please refer to my repo
I personally like Toni Petrina's approach (see https://coderwall.com/p/wkzizq/generic-eventargs-class). It differs from the accepted answer in that you don't have to create a special EventHandler class (e.g. LoginCompletedEventArgs).
(Note: I am using VS 2015 and C# v6. In older versions of Visual Studio and C#, you may have to add using System.Linq;)
Create a generic EventArgs<T> class that inherits from EventArgs...
class EventArgs<T> : EventArgs {
public T Value { get; private set; }
public EventArgs(T val) {
Value = val;
}
}
Declare your event handler...
public event EventHandler<EventArgs<object>> LoginCompleted;
Assuming you have declared and assigned an object named loginObject, add code to raise you event...
private void RaiseLoginCompleted() {
if (LoginCompleted != null)
LoginCompleted(this, new EventArgs<object>(loginObject));
}
In your client code, add the LoginCompleted event handler (uses Linq and calls a local method)...
LoginCompleted += (o, e) => onLoginCompleted(e.Value); // calls a local method
void onLoginCompleted(LoginObject obj) {
// add your code here
}
sometimes it sucks to create a class for merely passing a bool as a derived EventArgs! so you can simply use Action instead of EventHandler. you can pass any type and how many parameters you like (Action supports Up to 16).
class Raiser
{
public event Action<Raiser, bool,DateTimeOffset> OnCreate;
public void Create()
{
OnCreate?.Invoke(this, true,DateTimeOffset.Now);
}
}
class Listener
{
Raiser raiser;
public Listener()
{
raiser = new Raiser();
raiser.OnCreate += Raiser_OnCreate;
}
private void Raiser_OnCreate(Raiser arg1, bool arg2,DateTimeOffset dateTimeOffset)
{
throw new NotImplementedException();//Do Your works here
}
}
generally using Action and 'Func' are easier than Delegate.

EventHandler with custom arguments

I've been looking for an answer for about an hour on Google but I did not found exactly what I'm looking for.
Basically, I have a static Helper class that helps perform many things I do frequently in my App. In this case, I have a method named "CreateDataContextMenu" that creates a context menu on a given TreeView control.
public static void CreateDataContextMenu(Form parent, TreeView owner, string dataType)
{ ... }
TreeView owner is the control in which I will associate my context menu.
Then later on I add a Click event to a MenuItem like this:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click);
The problem I have here is that I want to pass "owner" and "dataType" as arguments to the menuItemFolder_Click event.
I tried the following:
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
(...)
private static void menuItemFolder_Click(object sender, System.EventArgs e, Treeview owner, string dataType)
{...}
But it doesn't work at all. It might be very naive of me to do it that way but I"m not very comfortable with event handler yet.
Any idea on how I could do that?
My first guess is that I need to create my own EventHandler for this specific case. Am I going in the right direction with that?
You should create a lambda expression that calls a method with the extra parameters:
menuItemFolder.Click += (sender, e) => YourMethod(owner, dataType);
Honest admission up front: I have not tried the code below.
I think the reason
menuItemFolder.Click += new System.EventHandler(menuItemFolder_Click(sender,e,owner,dataType));
won't work is because you are actually passing to System.EventHandler () the result of the invocation of menuItemFolder_Click () with the parameters provided. You are not passing a pointer to the function itself.
Try to write another function that implements the details of menuItemFolder_Click (). See if something like
private void menuItemFolder_Click_Helper (object sender, EventArgs e, object Owner, object DataType) {
// implement details here
}
and then call the function from within menuItemFolder_Click ().
I think the simplest code would be this:
EventHandler myEvent = (sender, e) => MyMethod(myParameter);//my delegate
myButton.Click += myEvent;//suscribe
private void MyMethod(MyParameterType myParameter)
{
//Do something
//if only one time
myButton.Click -= myEvent;//unsuscribe
}
Passing custom args into an event handler is not too difficult. Below is a clean and easily reusable method of doing so. Check it:
public class MyClass
{
public CustomArgsEventHandler MyEvent1;
public MyClass(){MyEvent1+=observer;}
public void observer(object sender, CustomEventArgs e){print(e.myArg);}
//...
}
//place in the same file if you like!
public class CustomEventArgs : EventArgs
{
public float myArg {get;set;}
public CustomEventArgs (float d) { myArg = d; }
}
public delegate void CustomArgsEventHandler (object sender, CustomEventArgs e);

How can I pass an event to a function in C#?

I am looking to pass an event to a helper function. This function will attach a method to the event. However, I am having trouble properly passing the event. I have tried passing a EventHandler<TEventArgs>. It compiles, but events are not attached (but are still added; it seems a copy of the event handler is made).
For example, if I have this:
public event EventHandler<EventArgs> MyEvent;
And the helper function:
public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo)
{
eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); };
}
And the caller:
MyHelperFunction(MyEvent);
MyEvent(null, new EventArgs()); // Does nothing.
The reason why this does not work is += when applied to a delegate creates a new delegate which is the combination of the old and the new. It does not modify the existing delegate.
In order to get this to work you will have to pass the delegate by reference.
public static void Helper(ref EventHandler<EventArgs> e)
{
e+= (x,y) => {};
}
The reason this works outside of the method is because the LHS is still the actual field. So += will create a new delegate and assign back to the member field.
Just came up with this little helper. If it is your self-created Event you could use a wrapper like this. You can use your += operators to attach handlers as normal but can pass the wrapper around and even raise the event from elsewhere.
public class GenericEvent<T> where T:EventArgs
{
public event EventHandler<T> Source = delegate { };
public void Raise(object sender, T arg = default(T))
{
Source(sender, arg);
}
public void Raise(T arg = default(T))
{
Source(this, arg);
}
public void AddHandler(EventHandler<T> handler)
{
Source += handler;
}
public void RemoveHandler(EventHandler<T> handler)
{
Source -= handler;
}
public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler)
{
genericEvent.AddHandler(handler);
return genericEvent;
}
}
Create the event like:
public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>();
Attach handlers:
MyEvent += (s,e) => {};
Raise event:
MyEvent.Raise();
Just guessing: Have you tried passing it as ref?
public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo)
MyHelperFunction(ref MyEvent);
It's not exactly nice, but you can use reflection to do this.
public EventMonitor(object eventObject, string eventName)
{
_eventObject = eventObject;
_waitEvent = eventObject.GetType().GetEvent(eventName);
_handler = new EventHandler(SetEvent);
_waitEvent.AddEventHandler(eventObject, _handler);
}
Where eventObject is the object containing the event, and eventName is the name of the event.
SetEvent is your event handler.
I also have a dispose method like this:
public void Dispose()
{
_waitEvent.RemoveEventHandler(_eventObject, _handler);
}
I have a solution where I have an two interfaces. The first interface has methods for binding certain events, while the other interface has event methods that can be bound to those events.
The first interface's bind methods takes the second interface as parameter, which makes it possible to bind the events to the event methods of any class that implements the second interface.
Is that understandable, or would you prefer some code? :)
As many have pointed out, passing an event to a method is either not possible or not simple.
Please clarify, but I suspect your intended usage will look something like:
void Register()
{
var super = new SuperHandler();
//not valid syntax:
super.HandleEvent(MyEvent1);
super.HandleEvent(MyEvent2);
super.HandleEvent(MyEvent3);
super.HandleEvent(MyEvent4);
}
You can accomplish this simply by making your intended generic event handlers accessible publicly (or internally, if you desire):
public static class GenericHandler
{
public static void HandleAnyEvent(object sender, EventArgs e)
{
//handle
}
}
public class SomeClass
{
void RegisterEvents()
{
var r = new EventRaiser();
r.ImportantThingHappened += GenericHandler.HandleAnyEvent;
}
}
In this example my catch-all handler is in a static class, but you can just as well use a non-static class. Also, I see that in your example you have made the method generic (TEventArgs). Because all EventHandler derivatives (such as CancelEventHandler) match the base EventHandler, you do not need to involve generics (nor would it be helpful).
If the registration logic is complex or you must keep the EventHandler private, consider using Interface Events. This may not meet your intended goal of reducing the amount of code, but it will allow you to create a class that can predictably handle all of the events of a specific type.
interface IRaiseEvents
{
event EventHandler ConnectionCreated;
event EventHandler ConnectionLost;
}
public class SuperHandler
{
void RegisterEvents(IRaiseEvents raiser)
{
raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected.");
raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected.");
}
}
Pass something like Action e = e => myevent += e;
And call from method with the handler? It has the benefit of working with .NET classes.

Calling C# events from outside the owning class?

Is it possible under any set of circumstances to be able to accomplish this?
My current circumstances are this:
public class CustomForm : Form
{
public class CustomGUIElement
{
...
public event MouseEventHandler Click;
// etc, and so forth.
...
}
private List<CustomGUIElement> _elements;
...
public void CustomForm_Click(object sender, MouseEventArgs e)
{
// we might want to call one of the _elements[n].Click in here
// but we can't because we aren't in the same class.
}
}
My first thought was to have a function similar to:
internal enum GUIElementHandlers { Click, ... }
internal void CustomGUIElement::CallHandler(GUIElementHandler h, object[] args) {
switch (h) {
case Click:
this.Click(this, (EventArgs)args[0]);
break;
... // etc and so forth
}
}
It's a horribly ugly kludge, but it should work... There must be a more elegant solution though? The .NET library does this all the time with message handlers and calling events in Control's. Does anyone else have any other/better ideas?
You just need to add a public method for invoking the event. Microsoft already does this for some events such as PerformClick for controls that expose a Click event.
public class CustomGUIElement
{
public void PerformClick()
{
OnClick(EventArgs.Empty);
}
protected virtual void OnClick(EventArgs e)
{
if (Click != null)
Click(this, e);
}
}
You would then do the following inside your example event handler...
public void CustomForm_Click(object sender, MouseEventArgs e)
{
_elements[0].PerformClick();
}
The event keyword in c# modifies the declaration of the delegate. It prevents direct assignment to the delegate (you can only use += and -= on an event), and it prevents invocation of the delegate from outside the class.
So you could alter your code to look like this:
public class CustomGUIElement
{
...
public MouseEventHandler Click;
// etc, and so forth.
...
}
Then you can invoke the event from outside the class like this.
myCustomGUIElement.Click(sender,args);
The drawback is that code using the class can overwrite any registered handlers very easily with code like this:
myCustomGUIElement.Click = null;
which is not allowed if the Click delegate is declared as an event.
You can shorten the code suggested in the accepted answer a lot using the modern syntax feature of the .NET framework:
public event Action<int> RecipeSelected;
public void RaiseRecpeSelected(int recipe) => RecipeSelected?.Invoke(recipe);
You really should wrap the code you want to be able to execute from the outside in a method. That method can then do whatever your event would do - and that event would also instead call that method.

Categories

Resources