I want to be able to have an object add one of its methods to an EventHandler that is passed to it and give said method the ability to remove itself from the EventHandler.
public class EventRaiser {
public event EventHandler event1
public event EventHandler event2
public void fire() {
event1?.Invoke(this, null);
event2?.Invoke(this, null);
}
}
public class EventSubscriber {
EventHandler eh;
public EventSubscriber(EventHandler eh) {
this.eh = eh;
eh += receive;
}
public void receive(object obj, EventArgs data) {
// Do stuff.
if(condition) eh -= receive;
}
}
public class MainClass {
public void Main() {
EventRaiser er = new EventRaiser();
EventSubscriber es1 = new EventSubscriber(er.event1);
EventSubscriber es2 = new EventSubscriber(er.event2);
er.fire();
}
}
The above code does not compile as I cannot even pass er.event1 or er.event2 to EventSubscriber ("The event can only appear in the left hand side of +=..."). Removing the event keyword from the EventHandlers fixes this issue but unsubscribing does not work properly. Is there a way to make this work? Use pointers maybe?
The problem here comes from you passing an EventHandler, not the list holding the delegates behind it itsself. Basically the "list of method pointers" to your handlers.
As you can see, in the declaration of event1 you have the keyword event, which is missing when you pass it somewhere else.
Unfortunately you cannot extract the "delegate holder" of an event easily.
Basically at the time you want to register your handler to an event you somehow need a compile time reference to it, in order to be able to += and -= to it.
You could do the following:
public class EventRaiser
{
public delegate void Event1(string args);
public List<Event1> handlers = new List<Event1>();
public void register(Event1 handler)
{
handlers.Add(handler);
}
public void unregister(Event1 handler)
{
handlers.Remove(handler);
}
public void fire()
{
handlers.ForEach(handler => handler("myEventArgs"));
}
}
public class EventSubscriber
{
Action<Event1> registerAction;
Action<Event1> unregisterAction;
public EventSubscriber(Action<Event1> register, Action<Event1> unregister)
{
registerAction = register;
unregisterAction = unregister;
registerAction(receive);
}
public void receive(string args)
{
// Do stuff.
unregisterAction(receive);
}
}
public class MainClass
{
public void Main()
{
EventRaiser er = new EventRaiser();
EventSubscriber es1 = new EventSubscriber(er.register, er.unregister);
er.fire();
}
}
Related
In my code for the PluginManager the event PluginEvent gets triggered after
a plugin has been added. But I want to get the event also triggered in the test class.
Somehow I cant solve this problem. The event only gets triggered in the PluginManager class. I read some articles how to create events and so on, but I got even more confused
PluginManager class
public class PluginEventArgs
{
public PluginEventArgs(string s) { Text = s; }
public String Text { get; private set; } // readonly
}
public class PluginManager
{
// Declare the delegate (if using non-generic pattern).
public delegate void PluginEventHandler(object sender, PluginEventArgs e);
// Declare the event.
public event PluginEventHandler PluginEvent;
protected virtual void RaiseSampleEvent(string message)
{
if (PluginEvent != null)
PluginEvent(this, new PluginEventArgs(message));
}
public PluginManager()
{
PluginEvent += PluginManager_PluginEvent;
SomeMethod();
}
void PluginManager_PluginEvent(object sender, PluginEventArgs e)
{
//This event gets triggered =)
}
public void SomeMethod()
{
//Code
RaiseSampleEvent("Name of the Plugin");
//Code
}
}
My test class:
class test
{
public test()
{
PluginManager pluginMg = new PluginManager();
pluginMg.PluginEvent += pluginMg_PluginEvent;
}
//I want this event to get triggered when a new plugin has been found
void pluginMg_PluginEvent(object sender, PluginEventArgs e)
{
MessageBox.Show(e.Text);
}
}
How can I manage to get the event triggered in the test class?
Thanks for any advise!
You're actually doing things right except for one logical Mistake.
In your test class you're creating the PluginManager by using the constructor. The constructor of PluginManager first subscribes to the event and then raises it.
AFTERWARDS you're subscribing to that event.
The simple Problem is that when you are raising the event your test class has not subscribed yet. When you raise that event again everything should work out just fine.
Another thing is that I would use the generic EventHandler class instead of creating your own delegates. This keeps your code cleaner and everyone knows that this is meant to be an event at first glance.
Just inherit PlugInEventArgs from EventArgs and then use EventHandler.
In your PluginManager class you shouldn't subscribe to your own event PluginEvent, you should subscribe to an external event or just raise the PluginEvent.
Let me give you an example:
public class PluginEventArgs
{
public PluginEventArgs(string s) { Text = s; }
public String Text { get; private set; } // readonly
}
public class OtherClass
{
public event PluginEventHandler PluginEvent;
private void RaiseEvent()
{
if (null != PluginEvent)
PluginEvent(this, new PluginEventArgs("some message"));
}
}
public delegate void PluginEventHandler(object sender, PluginEventArgs e);
public class PluginManager
{
public event PluginEventHandler PluginEvent;
private OtherClass otherClass;
protected virtual void RaiseSampleEvent(string message)
{
if (PluginEvent != null)
PluginEvent(this, new PluginEventArgs(message));
}
public PluginManager(OtherClass otherClass)
{
this.otherClass = otherClass;
this.otherClass.PluginEvent += otherClass_PluginEvent;
SomeMethod();
}
void otherClass_PluginEvent(object sender, PluginEventArgs e)
{
if (PluginEvent != null)
PluginEvent(sender, e); // this way the original sender and args are transferred.
}
public void SomeMethod()
{
//Code
RaiseSampleEvent("Name of the Plugin");
//Code
}
}
class test
{
public test()
{
OtherClass otherClass = new OtherClass();
PluginManager pluginMg = new PluginManager(otherClass);
pluginMg.PluginEvent += pluginMg_PluginEvent;
}
//I want this event to get triggered when a new plugin has been found
void pluginMg_PluginEvent(object sender, PluginEventArgs e)
{
MessageBox.Show(e.Text);
}
}
In my silverlight application, I am passing a method GetListCallBack to a delegate parameter of another method GetEmployees in Repository class which attaches that delegate as eventhandler to completed event of an async service call.
EmpViewModel Class:
public class EmpViewModel
{
private IRepository EMPRepository = null;
//constructor
public EmpViewModel
{
this.EMPRepository= new Repository();
}
public void GetList()
{
this.EMPRepository.GetEmployees(xyz, this.GetListCallBack);
}
public void GetAnotherList()
{
this.EMPRepository.GetEmployees(pqr, this.GetAnotherListCallBack);
}
private void GetListCallBack(object sender, GetListCompletedEventArgs args)
{
if (args.Error == null)
{
this.collection1.Clear();
this.collection1 = args.Result;
}
else
{
//do sth
}
}
public void GetAnotherListCallback(object sender, GetListCompletedEventArgs args)
{
//do sth with collection1
}
}
Repository Class:
public class Repository : IRepository
{
private readonly ServiceClient _client=null ;
public Repository()
{
_client = new ServiceClient(Binding, Endpoint);
}
public void GetEmployees(int xyz, EventHandler<GetListCompletedEventArgs> eventHandler)
{
_client.GetListCompleted -= eventHandler;
_client.GetListCompleted += new EventHandler<GetListCompletedEventArgs>(eventHandler);
_client.GetListAsync(xyz);
}
}
Now, when a call to the method GetList() has been completed and then if I call another method GetAnotherList() in same class EmpViewModel, then GetListCallBack method gets called again before GetAnotherListCallBack gets called.
This is probably happening as both methods get subscribed to the event.
As you can see, I have explicitly unsubscribed the eventhandler from the callback event but still the eventhandler is getting invoked.
Can anyone please suggest where I may be going wrong?
EDIT:
When I use a local variable instead of using this.EMPRepository to call the Repository method it works well as both CallBack methods are passed to different instances of Repository class and only the attched CallBack method gets fired
public class EmpViewModel
{
public void GetList()
{
EMPRepository = new Repository();
EMPRepository.GetEmployees(xyz, this.GetListCallBack);
}
public void GetAnotherList()
{
EMPRepository = new Repository();
EMPRepository.GetEmployees(pqr, this.GetAnotherListCallBack);
}
--------
First:
_client.GetListCompleted += new EventHandler<GetListCompletedEventArgs>(eventHandler);
is (seen from a functional point of view) the same as:
_client.GetListCompleted += eventHandler;
And now you immediate see the problem. Your code is:
_client.GetListCompleted -= eventHandler;
_client.GetListCompleted += eventHandler;
Why remove an eventhandler if you add it in the next line.
I guess you want to remove the old eventhandler and add a new one. So your function should get a delegate to the old eventhandler to remove. Something like this:
public void GetEmployees(int xyz, EventHandler<GetListCompletedEventArgs> oldEventHandler, EventHandler<GetListCompletedEventArgs> newEventHandler)
{
_client.GetListCompleted -= oldEventHandler;
_client.GetListCompleted += newEventHandler;
_client.GetListAsync(xyz);
}
But is that even possible?
If you have control over ServiceClient.GetListCompleted, why not remove the event keyword and just assign that delegate, like:
public void GetEmployees(int xyz, EventHandler<GetListCompletedEventArgs> eventHandler)
{
_client.GetListCompleted = eventHandler;
_client.GetListAsync(xyz);
}
Or... if the delegate is only called once per GetListASync:
public void GetEmployees(int xyz, EventHandler<GetListCompletedEventArgs> eventHandler)
{
_client.GetListCompleted += (sender, args) =>
{
eventHandler(sender, e);
_client.GetListCompleted -= eventHandler;
};
_client.GetListAsync(xyz);
}
This has turned out to be quite a lengthy question, so thank you in advance to all those who give up their time to read it and comment/answer :)
Edits
This Question has been majorly simplified.
Example code is now a complete, simple program
I am using an observer pattern implemented through interfaces:
public interface IObserver<in T>where T:EventArgs
{
void Update(object sender, T e);
}
public interface ISubject<in T, TU>where TU:EventArgs
{
event EventHandler<TU> Notify;
T State { set; }
void Attach(Action<object,TU> callback);
void Detach(Action<object, TU> callback);
}
I have created two simple classes which implement these interfaces
The MyObserver object will simply output a string to the console window when a Notify event is raised in the MySubject object.
public class MyObserver:IObserver<TestEventArgs>
{
private ISubject<bool, TestEventArgs> _subject;
public MyObserver(ISubject<bool, TestEventArgs> subject)
{
_subject = subject;
}
public void Subscribe()
{
_subject.Attach(Update);
}
public void Unsubscribe()
{
_subject.Detach(Update);
}
public void Update(object sender, TestEventArgs e)
{
Console.WriteLine(e.TestMessage);
}
}
public class MySubject:ISubject<bool, TestEventArgs>
{
public void ObservableEvent(string message)
{
InvokeNotify(message);
}
private void InvokeNotify(string message)
{
EventHandler<TestEventArgs> handler = Notify;
if(handler != null)
{
handler(this, new TestEventArgs(message));
}
}
public event EventHandler<TestEventArgs> Notify;
public bool State
{
set { throw new NotImplementedException(); }
}
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
public void Detach(Action<object, TestEventArgs> callback)
{
Notify -= new EventHandler<TestEventArgs>(callback);
}
}
public class TestEventArgs:EventArgs
{
public TestEventArgs(string message)
{
TestMessage = message;
}
public string TestMessage { get; private set; }
}
This test program shows that:
before myObserver has subscribed to the event no message is output to the Console window.
after myObserver has subscribed to the Notify event the message is output to the Console window.
after myObserver has UNsubscribed from the Notify event the message is still output to the Console window
static void Main(string[] args)
{
MySubject mySubject = new MySubject();
MyObserver myObserver = new MyObserver(mySubject);
//we have not subscribed to the event so this should not be output to the console
mySubject.ObservableEvent("First Test");
myObserver.Subscribe();
//we are now subscribing to the event. This should be displayed on the console window
mySubject.ObservableEvent("Second Test");
myObserver.Unsubscribe();
//We have unsubscribed from the event. I would not expect this to be displayed
//...but it is!
mySubject.ObservableEvent("Third Test");
Console.ReadLine();
}
The issue I'm having is that the unsubscribe process is not working.
I really don't understand why.
Questions
Why is the unsubscribe process not working?
What happens when comparing 2 event handlers? How are they defined as equal or not? This may lead to an answer to why the invocation list Contains method always returns false.
I suspect your problem is that this code:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
Actually allocates a new object, as does the corresponding Detach code. So what's being detached isn't the same thing as what's being attached.
I'm not sure, but you might be able to fix it by changing your Attach and Detach so that they're:
void Attach(EventHandler<TU> callback);
void Detach(EventHandler<TU> callback);
And in the client code:
public void Attach(EventHandler<TestEventArgs> callback)
{
Notify += callback;
}
public void Detach(EventHandler<TestEventArgs> callback)
{
Notify -= callback;
}
I haven't actually tried to compile this, but it looks like it should work.
Or, if the compiler can do the type conversion:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += callback;
}
Might be worth a shot.
I have created a very simple dummy program to understand Delegates and events. In my below program I am simple calling a method. When I call a method, five methods are automatically called with the help of delegates and events.
Kindly take a look at my program and do let me know where I am wrong or right as this is my first time using delegates and events.
using System;
namespace ConsoleApplication1
{
public delegate void MyFirstDelegate();
class Test
{
public event MyFirstDelegate myFirstDelegate;
public void Call()
{
Console.WriteLine("Welcome in Delegate world..");
if (myFirstDelegate != null)
{
myFirstDelegate();
}
}
}
class AttachedFunction
{
public void firstAttachMethod()
{
Console.WriteLine("ONE...");
}
public void SecondAttachMethod()
{
Console.WriteLine("TWO...");
}
public void thirdAttachMethod()
{
Console.WriteLine("THREE...");
}
public void fourthAttachMethod()
{
Console.WriteLine("FOUR...");
}
public void fifthAttachMethod()
{
Console.WriteLine("FIVE...");
}
}
class MyMain
{
public static void Main()
{
Test test = new Test();
AttachedFunction attachedFunction = new AttachedFunction();
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.firstAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.SecondAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.thirdAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.fourthAttachMethod);
test.myFirstDelegate += new MyFirstDelegate(attachedFunction.fifthAttachMethod);
test.Call();
Console.ReadLine();
}
}
}
Events are implemented using Delegates. That said by convention events take the form of:
void EventHandler(Object sender, EventArgs args);
EventHandler is actually a delegate defined in .Net. EventArgs is a class in .Net that acts as a placeholder to pass additional information. If you have additional information you would create a class that derived from EventArgs and contained properties for the additional data; therefore you would create your own delegate like so:
void MyEventHandler(Object sender, MyEventArgs args);
Microsoft has a tutorial on events here and also describes defining and raising events here
This is a common pattern with dealing with events:
// define the delegate
public delegate void CustomEventHandler(object sender, CustomEventArgs e);
// define the event args
public class CustomEventArgs : EventArgs
{
public int SomeValue { get; set; }
public CustomEventArgs( int someValue )
{
this.SomeValue = someValue;
}
}
// Define the class that is raising events
public class SomeClass
{
// define the event
public event CustomEventHandler CustomEvent;
// method that raises the event - derived classes can override this
protected virtual void OnCustomEvent(CustomEventArgs e)
{
// do some stuff
// ...
// fire the event
if( CustomEvent != null )
CustomEvent(this, e);
}
public void SimulateEvent(int someValue)
{
// raise the event
CustomEventArgs args = new CustomEventArgs(someValue);
OnCustomEvent(args);
}
}
public class Main
{
public static void Main()
{
SomeClass c = new SomeClass();
c.CustomEvent += SomeMethod;
c.SimulateEvent(10); // will cause event
}
public static void SomeMethod(object sender, CustomEventArgs e)
{
Console.WriteLine(e.SomeValue);
}
}
Try putting the line
public delegate void MyFirstDelegate();
inside the Test class.
Also, use the Invoke function on the event instead, i.e.
myFirstDelegate.Invoke();
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.