Is it possible to set, on a control (a linkbutton for example) an eventhandler which is not in the same context as the control creation?
For example, I have an instance of Class1 which instances Class2, the control is created in Class2, but I need the eventhandler to be in Class1.
Pseudo Code:
public class Class1
{
public Class1()
{
Class2 MyClass2 = new Class2();
MyClass2.DoSomething();
}
public void EventHandler()
{
// Handle the event
}
}
public class Class2
{
public void DoSomething()
{
SomeControl control = new SomeControl();
control.SomeEvent += parent.EventHandler;
}
}
Regards
Moo
Have your Class2 expose a custom public event. This event is triggered when the control event fires.
// In Class2
public event EventHandler<EventArgs<T>> ControlClickedEvent = null;
protected void OnControlClickedEvent()
{
if (ControlClickedEvent != null)
{
ControlClickedEvent(this, new EventArgs());
}
}
...
private void cmdButton_Click(object sender, EventArgs e)
{
OnControlClickedEvent();
}
Then have your Class1 subscribe to that event. The "event handler" is part of Class1.
// In Class1
MyClass2.ControlClickedEvent += new EventHandler<EventArgs<ControlClickedEvent>>(EventHandler);
If you are using multiple threads, ensure you use the InvokeRequired and BeginInvoke / Invoke methods in the code of the eventhandler in Class1.
Modifying darin's code:
public class Class1
{
public Class1()
{
Class2 class2 = new Class2();
class2.Control.SomeEvent += this.EventHandler;
}
public EventHandler()
{
//DoStuff
}
}
public class Class2
{
public Control control;
}
I have fallen out of favor of using straight events in C# these days for most communication like this. I prefer a more decoupled communication pattern, like the "Event Aggregator" which has many benefits over traditional event hooking. They include:
Reduced coupling (Class1 and Class2
don't need to know about each other)
Reduced event storms
Weak hookups (no memory leaks if you
forget to disconnect)
Filtering
public class Class1
{
public Class1()
{
new Class2().CreateControl(EventHandler);
}
public void EventHandler() {}
}
public class Class2
{
public void CreateControl(Action eventHandler)
{
SomeControl control = new SomeControl();
control.SomeEvent += eventHandler;
}
}
Related
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();
}
}
with the code posted below I want to update progressbar from foo1.
but I'm unable to implement eventhandler in Foo
class Foo : Form // implements progressbar
{
IFoo foo = new Foo1()
// this will not do:
ProgressBarEventHandler = new EventUpdateProgressBar(this.UpdateProgressBar);
UpdateProgressBar() { }
}
public delegate void EventUpdateProgressBar();
class FooBase
{
public EventUpdateProgressBar ProgressBarEventHandler;
protected virtual void UpdateProgressBar()
{
if (ProgressBarEventHandler != null)
ProgressBarEventHandler();
}
}
class Foo1 : IFoo,FooBase { base.UpdateProgressBar() }
class Foo2 : IFoo,FooBase {}
interface IFoo {}
is there a way to get this working or is there a better approach ?
I'm not completely sure what your intent was, but if you're trying to implement two classes, one of which raises events while the other handles them, then the minimal sample would look as follows.
delegate void MyEvent();
class MyEventSource
{
public event MyEvent Event;
public void RaiseEvent()
{
MyEvent evt = Event;
if (evt != null)
evt();
}
}
class MyEventListener
{
public void SubscribeForEventFromMyEventSource(MyEventSource eventSource)
{
eventSource.Event += this.EventHandler;
}
public void EventHandler()
{
// Event handling logic here
}
}
More reading on events is available here: https://msdn.microsoft.com/en-us/library/9aackb16(v=vs.110).aspx and here https://codeblog.jonskeet.uk/2015/01/30/clean-event-handlers-invocation-with-c-6/
I want an abstract class that raises an event, this event will be raised by the concrete class.
What I want is when I use another class to listen to these events the signature of the delegate should have the concrete type not the abstract, I don't want to cast it.
For the moment I have come up with this solution. It works but I don't find it particularly clever especially because of the "STUPID, DOESN'T MAKE SENSE......" part.
Here is my solution :
public delegate void ClassAEventHandler<TClassA>(TClassA classA) where TClassA : ClassA;
//Abstract class that raise Event
public abstract class ClassA<TClassA> : where TClassA : ClassA
{
public event ClassAEventHandler<TClassA> onClassEventRaised;
private TClassA eventClassA;
public void registerEventClass(TClassA classA)
{
this.eventClassA = classA;
}
public void raiseClassEvent()
{
this.onClassEventRaised(this.eventClassA);
}
}
// Exemple of concrete type
public class ClassB : ClassA<ClassB> // <------ IT SEEMS DUMB
{
public void action()
{
//Do something then raise event
this.raiseClassEvent();
}
public void saySomething() {};
}
// Exemple of concrete type
public class ClassC : ClassA<ClassC> // <------ IT SEEMS DUMB
{
public void command()
{
//Do something then raise event
this.raiseClassEvent();
}
public void destroySomething() {};
}
//Class that listen to the event raised
public class MyEventListener
{
private ClassB classB;
private ClassC classC;
public MyEventListener()
{
this.classB = new ClassB();
this.classB.registerEventClass(this.classB); // <------ STUPID, DOESN'T MAKE SENSE......
this.classB.onClassEventRaised += classB_onClassEventRaised;
this.classC = new ClassC();
this.classC.registerEventClass(this.classC); // <------ STUPID, DOESN'T MAKE SENSE......
this.classC.onClassEventRaised += classC_onClassEventRaised;
}
public void classB_onClassEventRaised(ClassB classB)
{
classB.saySomething();
}
public void classC_onClassEventRaised(ClassC classC)
{
classC.destroySomething();
}
//What i don't want
/*
public void classB_onClassEventRaised(ClassA classA)
{
((classB)classA).saySomething();
}
*/
}
First of all, you're not following regular event design in .NET.
Instead of implementing your own delegate, use EventHandler<TArgs>, and create a derived class of EventArgs.
Your CustomEventArgs should have a T generic parameter:
public class CustomEventArgs<T> where T : A
{
private readonly T _instance;
public CustomEventArgs(T instance)
{
_instance = instance;
}
public T Instance { get { return _instance; } }
}
Also, don't implement a custom way of registering events. If you want to encapsulate how handlers are added to the event, you need to use event accessors.
Finally, you could implement your classes as follows:
public class A<T> where T : A
{
private event EventHandler<CustomEventArgs<T>> _someEvent;
// An event accessor acts like the event but it can't be used
// to raise the event itself. It's just an accessor like an special
// event-oriented property (get/set)
public event EventHandler<CustomEventArgs<T>> SomeEvent
{
add { _someEvent += value; }
remove { _someEvent -= value; }
}
protected virtual void RaiseSomeEvent(CustomEventArgs<T> args)
{
// If C# >= 6
_someEvent?.Invoke(this, args);
// Or in C# < 6
// if(_someEvent != null) _someEvent(this, args);
}
}
public class B : A<B>
{
public void DoStuff()
{
// It's just about raising the event accessing the whole
// protected method and give an instance of CustomEventArgs<B>
// passing current instance (i.e. this) to CustomEventArgs<T>
// constructor.
RaiseSomeEvent(new CustomEventArgs<B>(this));
}
}
Now, if you try to handle SomeEvent, you'll get the CustomEventArgs<B> typed as B instead of A:
B b = new B();
b.SomeEvent += (sender, args) =>
{
// args.Instance is B
B instance = args.Instance;
};
b.DoStuff(); // Raises SomeEvent internally
I have the following scenario:
public abstract class SomeBaseClass
{
public event EventHandler SomeEvent;
...
}
public class SomeClass : SomeBaseClass
{
public void DoSomething()
{
//TODO
if (SomeEvent != null)
SomeEvent(this, EventArgs.Empty);
}
}
SomeBaseClass has an event which needs to be called in a base class, however this it isn't possible to directly call the event from a base class. To get around this, I can override the event in the base class, like so:
public class SomeClass : SomeBaseClass
{
new public event EventHandler SomeEvent;
This is fine I guess, however my question is whether there is some kind of universal method, or good practice for implementing the functionality above?
The fact that it isn't possible to call an event from a base class suggests that I shouldn't really be doing this in the first place, perhaps the responsibility of calling the event should be only in SomeBaseClass?
That isn't allowed indeed. If I may recommend an alternative approach:
public abstract class SomeBaseClass
{
public event EventHandler SomeEvent;
protected void RaiseSomeEvent(EventArgs e)
{
var eh = SomeEvent;
if (eh != null)
eh(this, e);
}
}
public class SomeClass : SomeBaseClass
{
public void DoSomething()
{
//TODO
RaiseSomeEvent(EventArgs.Empty);
}
}
Note that I have moved the invocation of the event handler to the owning class, this is required by .NET / C# since only that class can invoke the event handler. Second, I have made the event handler thread safe by assigning it to eh first.
Never hide the base class' event by using the new keyword! You will get unexpected results when you use the base class' type as type for a variable or when the base class invokes the event.
I would stay away from using new mainly because code will behave differently if an object is cast to the base class. Here's an alternative implementation:
public abstract class SomeBaseClass
{
public virtual event EventHandler SomeEvent;
protected virtual void HandleSomeEvent()
{
var ev = SomeEvent; // Localize event field used
if (ev != null)
{
ev(this, EventArgs.Empty);
}
}
}
public class SomeClass : SomeBaseClass
{
public override event EventHandler SomeEvent
{
add { base.SomeEvent += value; }
remove { base.SomeEvent -= value; }
}
protected override void HandleSomeEvent()
{
base.HandleSomeEvent();
// ... My own code here
}
}
This allows for a great deal of flexibility. You can provide some implementation of event handling as well as allow the implementer to completely override the base class implementation.
public delegate void ErrorHandler(string result);
public class BaseClass
{
public event ErrorHandler OnError;
protected void RaiseErrorEvent(string result)
{
OnError?.Invoke(result);
}
}
public class SampleClass:BaseClass
{
public void Error(string s)
{
base.RaiseErrorEvent(s);
}
}
I personally prefer to use delegates for that :
public abstract class SomeBaseClass
{
public event EventHandler SomeEvent;
protected Action<object, EventArgs> SomeEventInvoker;
public SomeBaseClass()
{
SomeEventInvoker = new Action<object, EventArgs>((sender, args) =>
{ if (SomeEvent != null) SomeEvent(sender, args); });
}
}
public class SomeClass : SomeBaseClass
{
public SomeClass()
{
DoSomething();
}
public void DoSomething()
{
SomeEventInvoker(this, new EventArgs());
}
}
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);
}
}