Pass additional parameters or objects using an Event Handler [duplicate] - c#

This question already has answers here:
Pass extra parameters to an event handler?
(10 answers)
Closed 8 years ago.
I feel like this is really basic, but I'm having trouble with this issue. I'm using a Process object and subscribing to a DataReceivedEventHandler. This event handler then delegates to another method, in this case "DoSomething", and the signature for the arguments is (Object sender, DataReceivedEventArgs args). What I need to do, is extend something, or provide something that will pass in additional information. Here is my current code:
// an object of some type
MyCustomObject obj = new MyCustomObject();
// set up obj and Process
process.OutputDataReceived += new DataReceivedEventHandler(DoSomething);
public void DoSomething(Object sender, DataReceivedArgs args)
{
// do some stuff, however, I need the "obj" object passed in for work
}
I feel like this is really trivial, but not sure how to proceed. I've read about subclassing the "EventArgs," but not sure how that will help, or how to even change the signature of "DoSomething" to accept a DataReceivedArgsExtended parameter, since the DataReceivedEventHandler is expecting a method with a DataReceivedArgs

Yes you can extend your DataReceivedArgs to DataReceivedArgsExtended, but remeber cast it into event handler method. For example:
public class MyObject
{
public event EventHandler<MyEventArgs> OnFire;
public void Fire()
{
if( OnFire != null )
{
//var e = new MyEventArgs { X=2 };
var e = new MyEventArgsNew { X = 3, Y = 4 };
OnFire( this, e );
}
}
}
public class MyEventArgs : EventArgs
{
public int X { get; set; }
}
public class MyEventArgsNew : MyEventArgs
{
public int Y { get; set; }
}
static void Main( string[] args )
{
var obj = new MyObject();
obj.OnFire += new EventHandler<MyEventArgs>( obj_OnFire );
obj.Fire();
Console.ReadLine();
}
static void obj_OnFire( object sender, MyEventArgs e )
{
var e2 = (MyEventArgsNew)e;
Console.WriteLine( e2.X );
Console.WriteLine( e2.Y );
}

Your question is already answered in the following questions:
C# passing extra parameters to an event handler?
How can I pass addition local object variable to my event handler? [duplicate]
In your case you need to use an anonymous delegate function or a lambda expression in the same scope and from within it call your own function, containing the event handle parameters and your additional ones:
// an object of some type
MyCustomObject obj = new MyCustomObject();
// set up obj and Process
process.OutputDataReceived +=
(Object _sender, DataReceivedArgs _args) =>
DoSomething(obj, _sender, _args);
public void DoSomething(Process process, Object sender, DataReceivedArgs args)
{
// do some stuff
}

Related

Why is "MyEvent" always make compiler error "CS0070"?

how to fixed CS0070 error?
Error:
Error CS0070 The event 'Demo.MyEvent' can only appear on the left hand side of += or -= (except when used from within the type 'Demo')
Code:
class Demo
{
public event EventHandler<int> MyEvent;
public void Handler(object sender, int arg)
{
Console.WriteLine($"I just go {arg}");
}
}
class Program
{
static void Main(string[] args)
{
var demo = new Demo();
var eventInfo = typeof(Demo).GetEvent("MyEvent");
var handlerMethod = demo.GetType().GetMethod("Handler");
var handler = Delegate.CreateDelegate(
eventInfo.EventHandlerType,
null,
handlerMethod
);
eventInfo.AddEventHandler(demo, handler);
demo.MyEvent?.Invoke(null, 312);
}
}
Error line:
demo.MyEvent?.Invoke(null, 312);
Field-like events (which this is) act like a field to the declaring type, but just appear like an event add/remove pair to external types. This means that only the type that declares the event can do things like access the current value, which is required in order to invoke the backing delegate. Basically, there's a hidden private field that the compiled declares that you can't see - and when you access the event from within the type, you're talking to the field directly. But when accessing the event from outside, you have to go via the accessors - and the only accessors that C# provides are the add and remove accessors.
If you write a method inside Demo, that method will be able to invoke the event.
Event must be invoked directly form it's class, if your scenario requires to invoke it from outside the event then simply encapsulate your event with a method:
public void InvokeMyEvent(int value)
{
MyEvent?.Invoke(this,value);
}
Then subscribe to it easily with a short code:
demo.MyEvent += MyEvent_EventHandeler;
private void My_EventHandeler(object sender, int e)
{
//enter code here
}
Or even shorter with lambda:
demo.MyEvent += (s, e) =>
{
//enter code here
}
Invoke it from anywhere:
demo.InvokeMyEvent(321);
Thanks Mr. Marc Gravell.
Excuse me, My code is wrong.
Correct code is:
namespace ConsoleApp1
{
class Demo
{
public event EventHandler<int> MyEvent;
public void Handler(object sender, int arg)
{
Console.WriteLine($"I just go {arg}");
}
public static void Main(string[] args)
{
var demo = new Demo();
var eventInfo = typeof(Demo).GetEvent("MyEvent");
var handlerMethod = demo.GetType().GetMethod("Handler");
var handler = Delegate.CreateDelegate(
eventInfo.EventHandlerType,
null,
handlerMethod
);
eventInfo.AddEventHandler(demo, handler);
demo.MyEvent?.Invoke(null, 312);
}
}
}

c# Adding a listener to Event with only the method name (text) [duplicate]

This question already has answers here:
AddEventHandler using reflection
(3 answers)
Closed 1 year ago.
I am trying to use Reflection to add a method from a class to an event. Here is my setup.
Have a class with a keyPressDown & keyPressUp method. I also have an Event in this class. keyPressDown & keyPressUp will fire whatever methods are subscribed to it (if any).
These additional methods are controlling RGB light emitting diodes. One method can flash a color, another can fade a color, etc..
I can subscribe to the event like myEventKeyUp += myClass.MethodA;
My problem is, I am storing the configuration the user wants in a database. So the only thing I can think of is storing the Method Name as text and use reflection to add it to the event handler.
Code Example:
Class MyClass
public event delegateKeyDown keyDownEvent;
public event delegateKeyUp keyUpEvent;
public void KeyUp()
{
joystick.SetBtn(false, 1, vJoyButtonID);
if (keyUpEvent != null) keyUpEvent();
}
public void KeyDown()
{
joystick.SetBtn(true, 1, vJoyButtonID);
// IF WE HAVE ANY LISTENERS THEN FIRE THEM
if (keyDownEvent != null) keyDownEvent();
}
public void MethodA()
{
// DO SOMeTHING HERE
}
Main Form
button.keyDownEvent += button.SetRandomColor;
button.keyUpEvent += button.TurnOff;
What I need to do is something like:
button.keyUpEvent += MyClass.GetType().GetMethod("MethodA");
I know you can't do what I am trying to do with Reflection, I read that I can use reflection to get hold of the delegate that contains the event handler, and add it through that way but I am unsure (or unclear about this).
In the vein of Jim W's link, here's a working example:
using System;
using System.Reflection;
internal class Program
{
private static void Main()
{
var foo = new Foo();
var fooType = foo.GetType();
var eventInfo = fooType.GetEvent("Bar");
var methodInfo = fooType.GetMethod("OnBar", BindingFlags.Static | BindingFlags.Public);
eventInfo.AddEventHandler(foo, Delegate.CreateDelegate(eventInfo.EventHandlerType, methodInfo));
foo.RaiseBar();
Console.ReadKey();
}
}
public class Foo
{
public delegate void BarHandler(object sender, BarEventArgs args);
public event BarHandler Bar;
public void RaiseBar()
{
Bar(this, new BarEventArgs());
}
public static void OnBar(object sender, BarEventArgs args)
{
Console.WriteLine(args.Guid);
}
}
public class BarEventArgs : EventArgs
{
public Guid Guid => Guid.NewGuid();
}
Thanks for both of your help. Between the two examples shown I was able to wrap my head around this. I Made a function to handle the event setup as follows:
private void SetupEventHandlers(RGBButton button,string EventName, string MethodName)
{
// THIS SETS UP THE EVENT HANDLER THAT WILL FIRE ANY ADDITION
// ACTIONS THE USER COULD WANT WHEN A BUTTON IS PRESSED OR RELEASED.
var p = button;
var eventInfo = p.GetType().GetEvent(EventName);
var methodInfo = typeof(RGBButton).GetMethod(MethodName);
try
{
// TRY TO SUBSCRIBE TO OUR KEYUP OR KEYDOWN EVENT
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, p, methodInfo);
eventInfo.AddEventHandler(p, handler);
}
catch (Exception)
{
// MOST LIKELY COULDN'T FIND THE METHOD WE ARE TRYING TO FIRE
throw new System.InvalidOperationException("Failed to find method: " + MethodName + "', which is registered as an Event Subscriber.");
}
}
This allows me to add to an event in my class (a few different events) with any given name (stored in a database) all from one function. the var P = button is a class which defines my RGB Led arcade buttons. That class contains the events that get triggered when the button is pressed.

NullReferenceException when attempting to fire an event

I'm learning about creating events and creating multi-threaded applications.
The method Thread is called by another class which populates the params with search conditions. A BackgroundWorker is created, performs a search and returns the results to worker_RunWorkerCompleted.
Within worker_RunWorkerCompleted, I want to send the results back to my UI which is subscribing to the Fireendofsearch event.
I'm having trouble understanding why my code below throws the following error
Object reference not set to an instance of an object.
when I fire the event Fireendofsearch
public class BackgroundSearch
{
public event SearchResultCompleteThreaded Fireendofsearch;
public EventArgs a = null;
public delegate void SearchResultCompleteThreaded(object seachresults, EventArgs a);
internal void Thread(string folder, string parms)
{
var Argument = new List<object> { folder, parms };
var worker = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = false};
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync(Argument);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var passedAugue = e.Argument as List<object>;
var returnResult = new List<string[]>();
if (passedAugue != null)
{
var result = Directory.GetFiles(passedAugue[0].ToString(), passedAugue[1].ToString(), SearchOption.AllDirectories);
foreach (string s in result)
{
var t = new string[4];
t[0] = s;
t[1] = File.GetCreationTime(s).ToString();
t[2] = File.GetLastAccessTime(s).ToString();
t[3] = File.GetLastWriteTime(s).ToString();
returnResult.Add(t);
}
}
if (returnResult.Count != 0)
{
e.Result = returnResult;
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
Fireendofsearch(e.Result, a);
}
}
}
Firendofsearch will be null until someone subscribes to it, change your work completed event handler to this to fix it.
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
var friendOfSearch = Fireendofsearch;
if(friendOfSearch != null)
friendOfSearch (e.Result, a);
}
}
The reason I copy it to a variable is if someone in another thread is the last person to unsubscribe between the null check and the raising of the event you will still get the null reference exception, by coping to another variable first it solves that problem.
However I would make some other changes if I where writing it, you are retuning a null EventArgs for some reason and passing the result back as the "Sender" in the traditional event pattern. I would change your code to this
public event EventHandler<FriendOfSearchArgs> FirendsOfSearch;
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
RaiseFriendOfSearch(e.Result);
}
}
protected virtual void RaiseFriendOfSearch(object result)
{
var friendOfSearch = FirendsOfSearch;
if(friendOfSearch != null)
friendOfSearch(this, new FriendOfSearchArgs(result));
}
public class FriendOfSearchArgs : EventArgs
{
public FriendOfSearchArgs(object result)
{
Result = result;
}
public object Result {get; private set;}
}
This was all written in the SO text box so there may be one or two errors.
You should check for null before trying to invoke the delegate. And you have to pull it into a separate variable first to avoid threading issues.
var ev = Fireendofsearch;
if ( ev != null ) ev( ... );
I have also found it useful to have an extension method for this case:
public static void Raise ( this EventHandler h, object sender )
{
if ( h != null) h( sender, EventArgs.Empty );
}
And then:
MyEvent.Raise ( this );
"Behind" your public event, there's an implicit private variable of type SearchResultCompleteThreaded. The type SearchResultCompleteThreaded is a delegate type.
In .NET all delegates are "multicast" delegates. That means they have an invocation list (your GetInvocationList() method on SearchResultCompleteThreaded is derived from System.Delegate.GetInvocationList().
Now, in .NET, the invocation list is guaranteed to consist of one or more items (not zero or more). Any delegate type is an immutable type. But if you try to create a new instance by "subtracting away" all members in the invocation list of an existing instance, as in var newDel = oldDel - oldDel; or reuseDel -= reuseDel;, then instead of getting a new instance with a zero-length invocation list, you get a null reference!
The good thing about this is that you don't have to worry about a subtle difference between an "empty" delegate instance (which could otherwise have been permitted) and a null reference. The bad thing about it, is the problem you had above.
For some reason (Optimization most likely), an event is only instantiated when a first handler method subscribe to it.
You must check for that in your code.
Here's how I usually declare an event:
public event SearchResultCompleteThreaded Fireendofsearch;
private void RaiseFireEndOfSearchEvent(EventArgs e)
{
if (Fireendofsearch != null)
{
Fireendofsearch(this, e);
}
}
And whenever I need to raise the event, I would just call the helper method instead.
Set:
public event SearchResultCompleteThreaded Fireendofsearch = delegate { };
Needs initialized possibly?

Is there anyway to define a single event handler for variety of events in C#?

I would like to declare common handler for variety of events, that will basically route events to the JavaScript part of the app. I myself come for JS/AS3 background, where something like this is a relatively simple thing to do. SilverLight and C# look quite stricter though.
The plan is like follows, JS will call a special [ScriptableMember] with a class name to instantiate and method to call, as string arguments. [ScriptableMember] then must contruct an instance of the class and optionally subscribe to the events, that can be fired by that instance. Events to subscribe to, will be exposed in public static struct of some kind. [ScriptableMember] will have to extract event properties from that struct and subscribe with the single event handler to all of them. Common single event handler then should translate C# events to JS ones.
Obviously everything should be generalized and automated in some kind of factory logic.
One big problem to this is that I cannot know beforehand what will be the type of delegate or EventArgs object. And even if I knew it would have been troublesome to create an overload for each and every case. Is there anyway to define a generic event handler in C#?
UPDATE:
Here is a code snippet that deals with described logic:
[ScriptableMember]
public void exec(string compName, string action, ScriptObject scriptObject)
{
String compFQName;
Type compClass;
object comp;
compFQName = "fully.qualified.name." + compName;
compClass = Type.GetType(compFQName);
if (compClass != null) {
comp = Activator.CreateInstance(compClass);
FieldInfo fieldInfo = compClass.GetField("dispatches", BindingFlags.Static | BindingFlags.Public);
object[] dispatches = (object[])fieldInfo.GetValue(comp);
if (dispatches != null) {
foreach (string eventName in dispatches) {
EventInfo eventInfo = compClass.GetEvent(eventName);
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "OnComponentEvent");
eventInfo.AddEventHandler(comp, handler);
}
}
}
// ...
}
And then there is an event handler that should catch up all the events:
public void OnComponentEvent(object sender, EventArgs args)
{
// do something ...
}
Every event args must be inherited from EventArgs class. If you follow this rule, you just can declare your handler like
public void GenericHandler(object sender, EventArgs e)
and subscribe it to any event
EDIT:
Like said O. R. Mapper, if the signature of your handler is incomparable with told above, you can call it from anonimous method that subscribed to your event, for example:
public delegate void StupidEventHandler(int walueThatMustBeWrappedToEventArgs);
public event StupidEventHandler StupidEventOccured;
//....
{
//....
stupidClass.StupidEventOccured += delegate(int value) { handlerObject.GenericHandler(stupidClass, new StupidEventHandlerWrapper(value)); }
}
EDIT:
Since the question suggests unusial .NET feature usage, handling would be unusual too. Since YOU subscribe this to everything you must know what events types can occure. Then handle it like this:
public void GenericHandler(object sender, EventArgs e)
{
if (e is MouseEventArgs)
{
var mouseArgs = e as MouseEventArgs;
// .. process this case
}
else if (e is ....)
}
EDIT:
To subscribe it try this:
[ScriptableMember]
public void exec(string compName, string action, ScriptObject scriptObject)
{
String compFQName;
Type compClass;
object comp;
compFQName = "fully.qualified.name." + compName;
compClass = Type.GetType(compFQName);
if (compClass != null) {
comp = Activator.CreateInstance(compClass);
FieldInfo fieldInfo = compClass.GetField("dispatches", BindingFlags.Static | BindingFlags.Public);
object[] dispatches = (object[])fieldInfo.GetValue(comp);
if (dispatches != null) {
foreach (string eventName in dispatches) {
EventInfo eventInfo = compClass.GetEvent(eventName);
EventHandler handlerMethod = OnComponentEvent;
var handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, handlerMethod.Target, handlerMethod.Method);
eventInfo.AddEventHandler(comp, handler);
}
}
}
// ...
}
Why your version doesn`t work is a great mistery.

How to pass an event to a method?

I would like to create a method that takes an event as an argument and adds eventHandler to it to handle it properly. Like this:
I have two events:
public event EventHandler Click;
public event EventHandler Click2;
Now I would like to pass a particular event to my method like this (pseudocode):
public AttachToHandleEvent(EventHandler MyEvent)
{
MyEvent += Item_Click;
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool.Click);
Is it possible?
I've noticed that this code worked fine, and returned to my project and noticed that when I pass an event declared in my class, it works, but when I pass event from other class it still does not work.
What I get is this error:
The event
'System.Windows.Forms.ToolStripItem.Click'
can only appear on the left hand side
of += or -=
My original answer was suitable from within the class that defined the event, but you've since updated your question to reflect that you wish to accomplish this from outside the defining class, so I've stricken that.
Only the class that defines an event can refer to the implicit delegate variable that the event uses. From outside that class, you only have access to the add and remove methods, via += and -=. This means that you can't do what you're asking, directly. You can, however, use a functional approach.
class A{
public event EventHandler Event1;
public void TriggerEvent1(){
if(Event1 != null)
Event1(this, EventArgs.Empty);
}
}
class B{
static void HandleEvent(object o, EventArgs e){
Console.WriteLine("Woo-hoo!");
}
static void AttachToEvent(Action<EventHandler> attach){
attach(HandleEvent);
}
static void Main(){
A a = new A();
AttachToEvent(handler=>a.Event1 += handler);
a.TriggerEvent1();
}
}
I did it like this:
public AttachToHandleEvent(Object obj, string EventName)
{
EventInfo mfi = obj.GetType().GetEvent(EventName);
MethodInfo mobj = mfi.GetAddMethod();
mobj.Invoke(obj, new object[] { Item_Click});
}
private void Item_Click(object sender, EventArgs e)
{
MessageBox.Show("lalala");
}
ToolStripMenuItem tool = new ToolStripMenuItem();
AttachToHandleEvent(tool "Click");
Thank you all for advice. This solution could not be done without your help.
It's not possible. You can use a delegate instead of an event if that meets your needs.
Just write tool.Click += Item_Click;
Edit: From MSDN "Events can only be invoked from within the class or struct where they (it) are declared". So what you are trying to do is not possible. Could you elaborate more on your needs? Why would you want to pass an event as a parameter?
delegate void doIt(object sender, object data);
event doIt OnDoIt;
void add(doIt theDel)
{
OnDoIt += theDel;
}
void doIt1(object a, object b)
{
}
void doIt2(object a, object b)
{
}
void add()
{
add(doIt1);
add(doIt2);
}
Your question suggests that you got some mechanisms wrong:
You can't pass events!
You most probably want to pass a function as a parameter, so the calling method will call that other method at some point. In technical terms this is a delegate. I suggest using the already defined Action class. Here's an example snippet:
void MyFunction (string otherArguments, Action onFinished){
...
if (onFinished != null)
onFinished.Invoke();
}
The nice thing about this is that when calling MyFunction you can declare the Action using the inline syntax:
MyFunction("my other argument", ()=>{
///do stuff here, which will be execuded when the action is invoked
});
I pass functions/methods (instead of events) like this:
class A
{
public void something()
{
var myAction =
new Action<object, object>((sender, evArgs) => {
MessageBox.Show("hiii, event happens " + (evArgs as as System.Timers.ElapsedEventArgs).SignalTime);
});
B.timer(myAction);
}
}
class B
{
public static void timer( Action<object, System.Timers.ElapsedEventArgs> anyMethod)
{
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(anyMethod);
myTimer.Interval = 2000;
myTimer.Start();
}
}
Giving an update to this question with an object oriented solution.
Instead of using an Action<EventHandler> that registers the event, you could create an object handling that for you
public class AEvent
{
private readonly A aInstance;
private AEvent(A instance) {
aInstance = instance;
}
public void Add(EventHandler eventHandler)
=> a.Event1 += eventHandler;
public void Remove(EventHandler eventHandler)
=> a.Event1 -= eventHandler;
public EventHandler Invoke => aInstance.Event1;
}
Then later on use that object like this:
static void Main(){
A a = new A();
AEvent aEvent = new AEvent(A)
aEvent.Add(handler);
a.Invoke();
}
One approach I haven't seen here would be to create an object which has delegates for subscribe and unsubscribe. Here is a complete example program.
class Program
{
private event EventHandler<EventArgs> eventHandler;
public static void Main(string[] args)
{
Program program = new Program();
Thing thing = new Thing(new EventWrapper<EventArgs>(
delegate(EventHandler<EventArgs> handler) { program.eventHandler += handler; },
delegate(EventHandler<EventArgs> handler) { program.eventHandler -= handler; }
));
// events are fired
program.eventHandler?.Invoke(program, EventArgs.Empty);
thing.Unsubscribe();
}
}
class Thing
{
private readonly Action<EventHandler<EventArgs>> _unsubscribeEventHandler;
public Thing(EventWrapper<EventArgs> eventHandler)
{
this._unsubscribeEventHandler = eventHandler.Unsubscribe;
eventHandler.Subscribe?.Invoke(OnEvent);
Console.WriteLine("subscribed");
}
private void OnEvent(object? sender, EventArgs e)
{
Console.WriteLine("event fired");
}
public void Unsubscribe()
{
_unsubscribeEventHandler?.Invoke(OnEvent);
Console.WriteLine("unsubscribed");
}
}
class EventWrapper<T> where T : EventArgs
{
public Action<EventHandler<T>> Subscribe { get; private set; }
public Action<EventHandler<T>> Unsubscribe { get; private set; }
public EventWrapper(Action<EventHandler<T>> subscribe, Action<EventHandler<T>> unsubscribe)
{
Subscribe = subscribe;
Unsubscribe = unsubscribe;
}
}
In this example, we created a new class called EventWrapper<T> which wraps delegates for += and -= and exposes them with Subscribe and Unsubscribe methods. The delegates will need to be created by the class which created the event.

Categories

Resources