I am trying to subscribe to an event that is in my base class but the method in my derived class doesn't seem to trigger whenever that event is triggered. Sample code is below.
public abstract class BaseClass
{
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler Event;
protected virtual void OnEvent(EventArgs ea)
{
if (this.Event!= null)
{
this.Event(null, ea);
}
}
}
public partial class DerivedClass : BaseClass
{
protected override void OnInit(EventArgs e)
{
base.Event+= DoSomething;
}
private void DoSomething(object sender, EventArgs e)
{
//Do Something here.
}
}
BaseClass.OnEvent is called in another control that has the same base class and the derived class where I want to subscribe to is inside another control. Is this possible?
BaseClass.OnEvent is called in another control that has the same base class and the derived class where I want to subscribe to is inside another control. Is this possible?
If you have a derived class and you have two instances of this derived class, the method of instance one won't be called if anything happens in instance two.
In your case you even have two different derived classes, sharing one base class and at runtime you have at least one instance of each derived class, which means, there is no communication between these two.
If you need to link two instances together you have to do something like this:
var instanceOne = new DerivedClassOne();
var instanceTwo = new DerivedClassTwo();
// When something in one happens, let two know:
instanceOne.OnEvent += (sender, e) => instanceTwo.ReactOnOtherChange();
Related
I'm writing down a customized Timer, that extends DispatcherTimer. It has a method called TimerSetUp, in which I associate the Tick event to an EventHandler.
public class MyTimer : DispatcherTimer
{
public MyTimer(){}
public void TimerSetUp(EventHandler<EventArgs> _EventHandler)
{
base.Tick += _EventHandler;
}
}
I have now another couple of classes, StopwatchTimer and CountdownTimer, that extend MyTimer. Let's look at CountdownTimer.
public class CountdownTimer : MyTimer
{
public CountdownTimer()
{
base.TimerSetUp(CountdownTimer_Tick);
}
public void CountdownTimer_Tick(object sender, EventArgs e)
{
//...do something
}
}
When either of them is declared, the constructor must call the base class method TimerSetUp and just set up the event handler.
The problem is that, in the signature of the TimerSetUp function, it gives me this error
Cannot implicitly convert type 'System.EventHandler<System.EventArgs>'
to'System.EventHandler<object>
And I can't get rid of it. If I just put TimerSetUp inside CountdownTimer, and just assign directly the event handler, like this
base.Tick += CountdownTimer_Tick;
it works, but I'd really like to make it work the other way.
So I'd like to know if
1) There's a way I can resolve this
2) Is this a good approach to extensibility of the code?
Nevermind, I made it. I just casted a generic object in the function signature and wrapped a new eventHandler inside the code.
public void TimerSetUp(EventHandler<object> _EventHandler)
{
base.Tick += new EventHandler<object>(_EventHandler);
}
DispatchTimer.Tick seems to be the non generic EventHandler rather than EventHandler<EventArgs>. Since you are using the base EventArgs anyway, they are functionally equivalent:
public void TimerSetUp(EventHandler _EventHandler)
{
base.Tick += _EventHandler;
}
See: https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer.tick(v=vs.110).aspx
I have a parent abstract class with several children classes. Eventually, I would like the progress done in the children classes to be shown via a progress bar in the GUI.
What I currently have done right now, which I am realizing will not work, is the event method definition declared in the parent class as a virtual method which each child class will overwrite. So something like :
public abstract class Parent
{
public event EventHandler someEvent;
protected virtual void OnSomeEvent(object sender, EventArgs e)
{
EventHandler eh= someEvent;
if (eh!= null)
{
eh(this, e);
}
}
}
And my child classes have something like :
protected override void OnSomeEvent(object sender, EventArgs e)
{
base.OnSomeEvent(sender, e);
}
and the event is raised somewhere in the child class.
However, seeing as the parent class is abstract, I will not be able to listen to the event from my GUI because I can not create an instance of an abstract class.
Am I completely off course and/or is there another method of doing this?
You can attach to the event from the child instance.
public abstract class Parent
{
public event Action Something;
public void OnSomething()
{
if (Something != null)
{
Something();
}
}
}
public class Child : Parent
{
}
Child c = new Child();
c.Something += () => Console.WriteLine("Got event from child");
c.OnSomething();
> Got event from child
You can even declare it as a Parent type that contains a child:
Parent c2 = new Child();
c2.Something += () => Console.WriteLine("Got event from Parent type");
c2.OnSomething();
> Got event from Parent type
An abstract class is just a code template that gets copied into every class that inherits from it (to put it simply). Think of it like, all of your Child classes contain an identical copy of the code that exists in Parent.
Note that this will also produce a unique event handler for each instance of Child. Having a static event handler for all Childs that derive from Parent would look like this, and requires no code in Child:
public abstract class Parent
{
public static event Action Something;
public static void OnSomething()
{
if (Something != null)
{
Something();
}
}
}
Then, you could do something like this, for example:
Parent.Something += () => Console.WriteLine("This will be invoked twice.");
Child c = new Child();
Child c2 = new Child();
c.OnSomething();
c2.OnSomething();
> This will be invoked twice.
> This will be invoked twice.
Both of those objects/event calls will invoke the same event handler even though they come from separate children.
First thing to not, because someEvent does not specify static, every instance of the child class will have its own someEvent. This means you aren't getting a unified view, but a diversified one. This is useful for responding to a button being pressed, since you don't want to respond the same way when they click the background.
Typically rather than using a class hierarchy, you would use composition to handle this kind of situation. For example, adding the following class to yours:
public class ParentContainer
{
private List<Parent> watched = new List<Parent>();
public void Add(Parent watch)
{
watched.Add(watch);
watch.SomeEvent += Handler;
}
private void Handler(object sender, EventArgs args)
{
//Do something
}
}
I am getting stumped with my plug-in architecture that I am trying to develop with respect to events. I can do this just fine, in a single application: (Obviously this is a very simplified version of what I am trying to accomplish, and if this were my code there are easier ways to accomplish this, just try to follow the logic ;)
public Form1()
{
public event EventHandler OnValueOver5Chars;
Main()
{
OnValueOver5Chars+= new EventHandler(WarnUser);
....
}
private void textBox_Changed( object sender, EventArgs e )
{
if( sender.Text.count() > 5 )
OnValueOver5Chars(sender, e); // WORKS HERE
}
private void WarnUser(object sender, EventArgs e)
{
...
}
}
However, now I have a plug-in architecture, where the plugin implements an interface which houses my event:
// Interface.cs
public interface IPlugin
{
event EventHandler OnValueOver5Chars;
...
}
// Plugin1.cs
public class Plugin1 : IPlugin
{
public event EventHandler OnValueOver5Chars;
Plugin1()
{
OnValueOver5Chars += new EventHandler(Plugin1WarnUser);
}
private void Plugin1WarnUser(object sender, EventArgs e)
{
...
}
}
// Form.cs
public class Form1 : Form
{
public Form1()
{
Assembly SampleAssembly = Assembly.LoadFrom("Plugin1.dll");
Type myType = SampleAssembly.GetTypes()[0];
if (myType.GetInterfaces().Contains(typeof(IPlugin)))
{
IPlugin myInstance = Activator.CreateInstance(myType) as IPlugin;
myInstance.OnValueOver5Chars(this, new EventArgs());
// Compiler Error CS0079 - The event 'event' can only appear on the left hand side of += or -=
}
}
????
You're trying to hook up an event to another event, and that won't work. You need to hook the plug-ins event to a method/delegate. Once you do that, have the method/delegate call the other event.
myInstance.OnValueOver5Chars += OnValueOver5CharsFunc;
...
/*In Form1*/
void OnValueOver5CharsFunc( object sender, EventArgs args )
{
OnValueOver5Chars( sender, args );
}
Events in C# have the property that they are not "callable" directly as methods (or as such as delegates) outside of the class where they are defined.
In your first example you are calling the event from within the class in which you define it. In the second example, however, you are trying to call OnValueOver5Chars from outside the class - hence the error.
To solve this you could consider adding a method to your IPlugin interface (e.g. ValueOver5Chars) that performs OnValueOver5Chars. Note that it is more common to name the event ValueOver5Chars (say), and provide a method OnValueOver5Chars to raise it (i.e. the other way round). See for example the Windows Forms Button class and its Click event.
public Class A
{
public A()
{
someotherclass.someevent += new EventHandler(HandleEvent);
}
private void HandleEvent(object sender,CustomEventArgs e)
{
if(e.Name == "Type1")
Method1();
else if(e.Name == "Type2")
Method2();
}
protected virtual void Method1(){}
protected virtual void Method2(){}
}
public class B: A
{
public B()
{ /*Something*/}
protected override void Method1(){/*some logic*/}
protected override void Method2(){/*some other logic*/}
}
public class C: A
{
public C()
{ /*Something*/}
protected override void Method1(){/*some logic*/}
protected override void Method2(){/*some other logic*/}
}
public class Main
{
private A;
public Main(){/*Something*/}
private void StartB()
{
A = new B();
}
private void StartC()
{
A = new C();
}
}
Now, what happens is, after I go through a cycle in which both the methods StartB(called first) and StartC(called second) are called, when the someevent is triggered, the code tries to execute the Method in Class B(and later Class C, I hope. I could not get there since it errors out when it calls method in Class B), instead which I want it to call only the method in Class C.
I think that, since the event is subscribed at constructor, Class B methods are still getting fired since it is subscribed initially on the call of StartB.
Question:
I want only the methods of the class that is instantiated the latest should be executed.
For Example: if StartB and StartC are called in order, when someevent is triggered the Methods in Class C should only get executed. Same Vice-Versa. How to do that?
I know am doing something terribly wrong. Any help is much appreciated.
You aren't unsubscribing from the event from your first instance so it will be called. If you don't want it to be called you need to unsubscribe. You could do something like this
class A
{
private static EventHandler lastHandler;
public A()
{
//warning, not thread safe
if(lastHandler != null)
{
someotherclass.someevent -= lastHandler;
}
lastHandler = new EventHandler(HandleEvent);
someotherclass.someevent += lastHandler;
}
but it seems pretty hacky. You are probably better off implementing a method (e.g. IDisposable) to clean up your last instance before a creating a new one.
If I understand you correctly you are saying the methods on B are being called after startC is called and you don't wish this to happen?
I'm guessing your issue is that someotherclass is a static class, or an instance is somehow being shared between all the created B's and C's - in which case you need to unregister the old event handler from someotherclass.someevent when you create the new class. If you don't unregister the handler then the someotherclass object will have a reference to the B or C object that registered with it, so even though you are overwriting the reference in the main class the object is still kept alive by the reference in the event and is still being called when the event is triggered.
I am bubbling events in my application and so therefore using the bubble events method. As this method handles all sorts of bubbled events their is a switch or if statement within it to determine what sort of event we're dealing with. I was wondering if I could get around this by creating different versions of the event args class. So let me explain, say I have two types of event that are handled differently called X and Y, I create new event args classes for these two events as they store different types of info.
public class EventsArgsX : EventsArgs
public class EventsArgsY : EventsArgs
then when I RaiseBubbleEvent from somewhere in my application I can pass either of the two event arg based types, so..
EventArgsX foox = new EventArgsX();
RaiseBubbleEvent(null,foox);
or
EventArgsY fooy = new EventArgsY();
RaiseBubbleEvent(null,fooy);
then the OnBubbleEvent method picks this up, who's signature is
override OnBubbleEvent(object source, EventArgs e)
now i cant overload this method as its overriden in the first place, so what I thought I could do was have another method with overloads in it to handle this, so
protected override OnBubbleEvent(object source, EventArgs e)
{
DoStuff(e);
}
private void DoStuff(EventArgsY args)
{}
private void DoStuff(EventArgsX args)
{}
but of course the problem is that EventArgs e in the OnBubbleEvent method is of type EventArgs at the time of calling. However we know its not. So how would i case it back to its actual type in order for the method call to work?
Many thanks, hope you can help me with this, its seems really easy like a might be missing something or that it just cant be done
any ideas??
It's simple:
protected override OnBubbleEvent(object source, EventArgs e)
{
if(e is EventArgsX)
DoStuff((EventArgsX)e);
else if(e is EventArgsY)
DoStuff((EventArgsY)e);
}
This, being KISS, is not very extensible. If you're planning on adding more event types, you can try double dispatch:
public abstract class EventArgsBase : EventArgs
{
public abstract void Bubble(IEventBubbler eb);
}
public interface IEventBubbler
{
Bubble(EventArgsX ex);
Bubble(EventArgsY ey);
}
public class EventArgsX : EventArgsBase
{
public virtual void Bubble(IEventBubbler eb)
{
eb.Bubble(this);
}
}
public class EventArgsY : EventArgsBase
{
public virtual void Bubble(IEventBubbler eb)
{
eb.Bubble(this);
}
}