I have 3 classes, one class triggers the event, another calls the event handler sender and finally the last receives the event. However the event handler in the sender class is always null, hence the receiver never handles the event. I cannot work out why it is null as I assign it as StartEventHandler as seen below.
I've tried to enter debug statements to see why the handler was never being run and found that it never gets to class C.
First Class triggers the event:
Class A {
public void Button_Click(object sender, RoutedEventArgs e)
{
B senders = new B();
senders.OnPageSwap(new StartEventArgs());
}
}
Second Class is the Sender
public delegate void StartEventHandler(object sender, StartEventArgs e);
public class B
{
public event StartEventHandler PageSwap;
public virtual void OnPageSwap(StartEventArgs e)
{
Console.WriteLine("Entered PageSwapSender");
if(PageSwap != null) PageSwap(this, e);
}
}
Third Class is the receiver
Class C {
B sender = new B();
void Connect()
{
sender.PageSwap += new StartEventHandler(this.sender_PageSwap);
Console.WriteLine("Entered Connect");
}
private void sender_PageSwap(object sender, StartEventArgs e)
{
Console.WriteLine("Entered Handler");
}
}
Can anyone tell me why PageSwap in class B is always null hence never running PageSwap(this, e).
The problem with your code is that you are instantiating two separate instances of the class B. In A you are trying to raise the event on one instance of B. In C you are trying to handle the event raised using a difference instance of B.
It seems that you have tried to do the right thing by creating a Connect method in C, but this is where you should have passed the existing instance of B through to C.
Had you written your code like this, it would have worked:
class A
{
public void Button_Click(object sender, RoutedEventArgs e)
{
B senders = new B();
C c = new C();
c.Connect(senders);
senders.OnPageSwap(new StartEventArgs());
}
}
public delegate void StartEventHandler(object sender, StartEventArgs e);
public class B
{
public event StartEventHandler PageSwap;
public virtual void OnPageSwap(StartEventArgs e)
{
Console.WriteLine("Entered PageSwapSender");
if (PageSwap != null) PageSwap(this, e);
}
}
class C
{
public void Connect(B sender)
{
sender.PageSwap += new StartEventHandler(this.sender_PageSwap);
Console.WriteLine("Entered Connect");
}
private void sender_PageSwap(object sender, StartEventArgs e)
{
Console.WriteLine("Entered Handler");
}
}
C.Connect() is not being called and it needs to, since it's where you are registering the EventHandler that calls this.sender_PageSwap. Without that, nothing will happen.
Related
I am new to events and have been trying to create one and succeed but I have one question.
I have created event like this:
public class CustomControl : Panel
{
public event EventHandler OutputChanged; //My event
public CustomControl()
{
InitializeComponents();
}
//This event raises inside richtextbox which is inside my panel
private void OnTextChanged(object sender, EventArgs e)
{
if (OutputUpdate == OutputUpdate.OnTextChanged)
{
ValidateText();
//This is my created event
OnOutputChanged(new OutputChangedEventArgs { Asd = "Something" });
}
}
//void for this event
protected virtual void OnOutputChanged(OutputChangedEventArgs e)
{
EventHandler handler = OutputChanged;
if(handler != null)
{
handler(this, e);
}
}
}
//Custom event args class for my event
public class OutputChangedEventArgs : EventArgs
{
public string Asd { get; set; }
}
Above code shows declaration of my event with custom class for EventArgs parameter and now I will show you how I implement it in my code:
customControl1.OutputChanged += OnOutputChanged;
private void OnOutputChanged(object sender, EventArgs e)
{
OutputChangedEventArgs args = e as OutputChangedEventArgs;
MessageBox.Show(args.Asd);
}
As you can see in my implantation I pass EventArgs and then I convert it to OutputChangedEventArgs and reason for that is because if I try private void OnOutputChanged(object sender, OutputChangedEventArgs e) I get error No overload for 'OnOutputChanged' matches delegate 'EventHandler'
So my question is how can I directly pass my custom EventArgs class so I do not need to convert it inside method that handles it?
You can use the generic version of EventHandler that allows the specification of the argument type.
public event EventHandler<OutputChangedEventArgs> OutputChanged;
I have a WinForms application wherein I have my main application with a separate class that is part of the solution. In the class which is defining a User control with Dev Express buttons, I have defined my event delegate, event, method and eventargs.
In the main program, i have defined my listener.
I am getting a null value in my event method and cannot see why. I have reviewed this a number of times and as far as I can see, it is completely correct.
I would appreciate any comments/corrections that would be useful here.
This is the code in my class.
public partial class XtraUserControl1 : XtraUserControl, IAnyControlEdit
{
public delegate void ButtonClickedEventHandler(object sender, ClickEventArgs e);
public event ButtonClickedEventHandler ButtonClicked;
public XtraUserControl1()
{
InitializeComponent();
}
public void OnButtonClicked(ClickEventArgs e)
{
if (ButtonClicked != null)
{
ButtonClicked(this, e);
}
}
public class ClickEventArgs : EventArgs
{
public readonly SimpleButton buttonClicked;
public ClickEventArgs(SimpleButton button)
{
this.buttonClicked = button;
}
}
This is the main code where I have defined the listener.
private void frmEHHeaders_Load(object sender, EventArgs e)
{
// Create the button group from the User Control XtraUserControl1 and add it to the grid repository
btnGroup = new User_Controls.XtraUserControl1();
RepositoryItemAnyControl riAny = new RepositoryItemAnyControl();
riAny.Control = btnGroup;
grdEHHeaders.RepositoryItems.Add(riAny);
colButtons.ColumnEdit = riAny;
// Add event handlers
this.grdEHHeaders.Views[0].MouseDown += gridView1_MouseDown;
gridView1.CustomRowCellEdit += GridView1_CustomRowCellEdit;
// Listener for the button class
btnGroup.ButtonClicked += new User_Controls.XtraUserControl1.ButtonClickedEventHandler(btnGroup_ButtonClicked);
GetData();
}
private void btnGroup_ButtonClicked(object sender, User_Controls.XtraUserControl1.ClickEventArgs e )
{
SimpleButton myButton = e.buttonClicked;
MessageBox.Show("You clicked " + myButton.Text);
}
I am trying to raise a event in one of classes of userControl, and Fire it in the Main class. I tried two different ways to fire this event, one of them works, But I still want to know why other way cannot work, and how to fix it.
My userContol class:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
return;
}
Class1 c = new Class1();
Thread accept = new Thread(
() =>
{
c.connection();
}
);
accept.Start();
}
}
And the Class1:
public class Class1
{
public delegate void myhandler(object sender, EventArgs e);
public event myhandler test;
public Class1()
{
}
public void connection()
{
test(this, new EventArgs());
}
}
In the Main, I just simply add into referent, and add
xmlns:my="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
then I try to subscribe this event in the main
public partial class SurfaceWindow1 : SurfaceWindow
{
/// <summary>
/// Default constructor.
/// </summary>
public SurfaceWindow1()
{
InitializeComponent();
Class1 c = new Class1();
c.test+=new Class1.myhandler(c_test);
// Add handlers for window availability events
AddWindowAvailabilityHandlers();
}
public void c_test(object sender, EventArgs e)
{
MessageBox.Show("fire");
}
}
If I only raise this event not into thread, it works fine, but If I try to let it raise in this thread, this test event only return null, and shows:
Object reference not set to an instance of an object.
looks like I did not subscribe it ever. So How to fix it if I must use it in thread.
This is a good example of what you really shouldn't be doing. You are trying to subscribe to an event of a class that is "owned" by another class. You are basically digging through the type hierarchy to find the event you want. This goes against good design.
Especially in UI applications there is a design concept called Event Bubbling. The type that owns the original object raising the event handles that event. In the handling of that event, it raises its own duplicate event. Here is a short-ish program that shows the concept:
public class RootEventClass
{
public event EventHandler SomeKindOfEvent;
protected virtual void OnSomeKindOfEvent(object sender, EventArgs e)
{
EventHandler handler = SomeKindOfEvent;
if (handler != null)
handler(this, e);
}
public void RaiseEvent()
{
Console.WriteLine("Root Event Firing");
OnSomeKindOfEvent(this, EventArgs.Empty);
}
}
public class FirstOwnerClass
{
private RootEventClass _rootClass;
public event EventHandler SomeKindOfEvent;
public FirstOwnerClass()
{
_rootClass = new RootEventClass();
_rootClass.SomeKindOfEvent += _rootClass_SomeKindOfEvent;
}
void _rootClass_SomeKindOfEvent(object sender, EventArgs e)
{
Console.WriteLine("First Owner Class Handling Root Owner Event");
OnSomeKindOfEvent(this, e);
}
protected virtual void OnSomeKindOfEvent(object sender, EventArgs e)
{
EventHandler handler = SomeKindOfEvent;
if (handler != null)
handler(this, e);
}
public void RaiseEvent()
{
_rootClass.RaiseEvent();
}
}
public class SecondOwnerClass
{
private FirstOwnerClass _firstClass;
public event EventHandler SomeKindOfEvent;
public SecondOwnerClass()
{
_firstClass = new FirstOwnerClass();
_firstClass.SomeKindOfEvent +=_firstClass_SomeKindOfEvent;
}
void _firstClass_SomeKindOfEvent(object sender, EventArgs e)
{
Console.WriteLine("Second Owner Class Handling First Owner Event");
OnSomeKindOfEvent(this, e);
}
protected virtual void OnSomeKindOfEvent(object sender, EventArgs e)
{
EventHandler handler = SomeKindOfEvent;
if (handler != null)
handler(this, e);
}
public void RaiseEvent()
{
_firstClass.RaiseEvent();
}
}
public class Program
{
public static void Main(string[] args)
{
SecondOwnerClass secondOwner = new SecondOwnerClass();
secondOwner.SomeKindOfEvent += secondOwner_SomeKindOfEvent;
secondOwner.RaiseEvent();
Console.ReadKey(true);
}
static void secondOwner_SomeKindOfEvent(object sender, EventArgs e)
{
Console.WriteLine("Got an event from the second owner defined in main");
}
}
If you run this program, you will get the following output:
Root Event Firing
First Owner Class Handling Root Owner Event
Second Owner Class Handling First Owner Event
Got an event from the second owner defined in main
So what is happening here. The Main method defines a SecondOwner object and then tells it to raise an event, which just passes it up the ownership chain to the root object. The magic is the event bubbling. The event is intercepted by each owning class and passed on. This way it can decide to pass it on or not.
Also, the Main method only needs to know about the events created in the SecondOwner class, it doesn't have to dig through SecondOwner, FirstOwner and finally RootEventClass.
Its an easy concept to grasp really, don't force your users (or yourself) to dig through types and ownership to get to an event, if users of the class will need that event, duplicate the event and bubble it.
I have four events:
View.AdditionPerformed += new EventHandler<EventArgs>(OnOperationPerformed);
View.SubtractionPerformed+=new EventHandler<EventArgs>(OnOperationPerformed);
View.DivisionPerformed+=new EventHandler<EventArgs>(OnOperationPerformed);
View.MultiplyPerformed+=new EventHandler<EventArgs>(OnOperationPerformed);
and one method:
private void OnOperationPerformed(object sender, EventArgs e)
{
}
How can I define which event raised my method? Something like this:
private void OnOperationPerformed(object sender, EventArgs e)
{
switch(event)
{
case MultiplyPerformed:{}
case DivisionPerformed:{}
...
}
}
Write your own EventArgs which has an enum inside, telling you the raised event.
enum MyEventEnum
{
AdditionPerformed,
SubtractionPerformed,
DivisionPerformed,
MultiplayPerformed
}
The EventArgs
class MyEventArgs : EventArgs
{
public MyEventEnum EventRaised { get; set; }
}
Define the Handlers
View.AdditionPerformed += new EventHandler<MyEventArgs>(OnOperationPerformed);
View.SubtractionPerformed+=new EventHandler<MyEventArgs>(OnOperationPerformed);
View.DivisionPerformed+=new EventHandler<MyEventArgs>(OnOperationPerformed);
View.MultiplyPerformed+=new EventHandler<MyEventArgs>(OnOperationPerformed);
When you call them:
this.AdditionPerformed(this, new MyEventArgs
{ EventRaised = MyEventEnum.AdditionPerformed };
I know it's pretty hardcoded, but there isn't any other way.
Instead of using EventArgs, you could use your own event argument class to pass in the necessary data to make the choice inside the handler.
It would then become available on your e variable inside the handler.
Cheers
I have a click event handler in class A with some logic. And now i want to access class A event handler from class B and do some logic so that class B event hadler logic fires first followed by class A event handler.
Example:
Class A
private void calculate_Click(object sender, System.EventArgs e)
{ this.MyMethod(); }
Class B
private void calculate_Click(object sender, System.EventArgs e)
{ // My new code.. (This should trigger first) this.MyMethod(); }
You may use event exposed by class A and consumed by class B like we do with Button class. Button exposes click event and in our form class we subscribe for click event being exposed by Button class.
I found this simple example for understanding here
using System;
namespace wildert
{
public class Metronome
{
public event TickHandler Tick;
public EventArgs e = null;
public delegate void TickHandler(Metronome m, EventArgs e);
public void Start()
{
// while (true) //uncomment this line if you want event to fire repeatedly
{
System.Threading.Thread.Sleep(3000);
if (Tick != null)
{
Tick(this, e);
}
}
}
}
public class Listener
{
public void Subscribe(Metronome m)
{
m.Tick += new Metronome.TickHandler(HeardIt);
}
private void HeardIt(Metronome m, EventArgs e)
{
System.Console.WriteLine("HEARD IT");
}
}
class Test
{
static void Main()
{
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
}
Assuming class B has instance member A instanceOfClassA initilized properly with an instance of A:
private void calculate_Click(object sender, System.EventArgs e)
{
// My new code.. (This should trigger first)
instanceOfClassA.MyMethod();
// other code
}
You may also consider inheriting class B from A:
class B:A
{
private void calculate_Click(object sender, System.EventArgs e)
{
// My new code.. (This should trigger first)
this.MyMethod(); // will come from base class A implementation.
// other code
}
}