I am writing a Class Library that will be used by other applications. I am writing it in C#.NET. I am having a problem with triggering events across classes. Here is what I need to do...
public class ClassLibrary
{
public event EventHandler DeviceAttached;
public ClassLibrary()
{
// do some stuff
OtherClass.Start();
}
}
public class OtherClass : Form
{
public Start()
{
// do things here to initialize receiving messages
}
protected override void WndProc (ref message m)
{
if (....)
{
// THIS IS WHERE I WANT TO TRIGGER THE DEVICE ATTACHED EVENT IN ClassLibrary
// I can't seem to access the eventhandler here to trigger it.
// How do I do it?
}
base.WndProc(ref m);
}
}
Then in the application that is using the class library I will do this...
public class ClientApplication
{
void main()
{
ClassLibrary myCL = new ClassLibrary();
myCL.DeviceAttached += new EventHandler(myCl_deviceAttached);
}
void myCl_deviceAttached(object sender, EventArgs e)
{
//do stuff...
}
}
You cannot do this. Events can only be raised from within the class that declares the event.
Typically, you'd add a method on your class to raise the event, and call the method:
public class ClassLibrary
{
public event EventHandler DeviceAttached;
public void NotifyDeviceAttached()
{
// Do processing and raise event
}
Then, in your other code, you'd just call myCL.NotifyDeviceAttached();
I think you should change your perspective on how events work. OtherClass should "own" the event and trigger it. ClassLibrary or ClientApplication (whichever you choose) "listens" to the event by "subscribing" to it and does a certain action when this event occurs.
How to implement this:
public class ClassLibrary
{
public OtherClass myOtherCl;
public ClassLibrary()
{
myOtherCl= new OtherClass();
myOtherCl.Start();
}
}
Trigger the event in the class where it logically happens, where it is detected.
public class OtherClass : Form
{
public event EventHandler DeviceAttached;
public Start()
{
// do things here to initialize receiving messages
}
protected override void WndProc (ref message m)
{
if (....)
{
OnDeviceAttach();
}
base.WndProc(ref m);
}
public void OnDeviceAttach()
{
if (DeviceAttached != null)
DeviceAttached ();
}
}
Finally, whoever needs to listen to the event needs access to the instance of the class holding the event, that is why myOtherCl was made public in this example.
public class ClientApplication
{
void main()
{
ClassLibrary myCL = new ClassLibrary();
myCL.myOtherCl.DeviceAttached += new EventHandler(myCl_deviceAttached);
}
void myCl_deviceAttached(object sender, EventArgs e)
{
//do stuff...
}
}
Event handlers can only be called directly by the class that declared them. If you need to call ClassLibrary.DeviceAttached from outside that class, you need to add a utility method like the following:
public void OnDeviceAttached()
{
DeviceAttached();
}
You may not want to use an event at all here. This is an oversimplification, but generally an event is something raised by a child component when it needs to communicate something back to its parent. In your case (I'm inferring from your code) your form is listening for a specific message (when a device is attached?), and when it spots that message it needs to tell myCL about it. For this purpose, you would instead just create a method in ClassLibrary and call it from your form.
Related
I am trying to understand for which purpose the events pattern decided that the method that fires the event should be declared virtual.
From C#6 in a Nutshell, from Joseph and Ben Albahari, O'Reilley:
Finally, the pattern requires that you write a protected virtual method that fires the
event. The name must match the name of the event, prefixed with the word On, and
then accept a single EventArgs argument:
Below a snippet I created to try to investigate.
I had the impression that the idea was to allow inheriting classes to completely overwrite how the event is handled, out of the box (original class). But the snippet below shows this is not possible, because deriving classes will never be able to invoke the event objects themselves (by the one one of the goals of the constraints imposed by the keyword event on delegates). The event can be invoked only in the containing class.
Now, since the pattern also asks that the method that fires the event simply check if the the event is not null and then call the delegate, with whatever every subscriber asked to do, what is left to be achieved by having the method that fires the event as virtual ? Inheriting classes are obliged to invoke the event the way it is in the broadcaster class, so all that is left for them is to add functionality. But this is exactly what they can achieve by subscribing to the event, in other words, by adding a call to an external function the time the event is fired.
I hope my wording is clear enough.
namespace eventsPatternVirtualEventFirerer
{
internal class Program
{
private static void Main(string[] args)
{
var obj = new X();
obj.ev += Obj_ev;
obj.Start();
}
private static void Obj_ev(object sender, EventArgs e)
{
Console.WriteLine("subscriber code...");
}
}
public class X
{
public event EventHandler<EventArgs> ev;
protected virtual void OnEvent(EventArgs e)
{
Console.WriteLine("original implementation...");
ev?.Invoke(this, e);
}
public void Start()
{
OnEvent(EventArgs.Empty);
}
}
public class X2 : X
{
public X2()
{
}
protected override void OnEvent(EventArgs e)
{
Console.WriteLine("inheriting class implementation overwrite...");
//compilation error - "the event 'X.ev' can only appear on the left hand side of += or -= (except when used from within the type 'X')"
ev?.Invoke(this, e);
}
}
}
I think the purpose is to allow derived classes to do something before/after the event is fired
public class X2 : X
{
public X2()
{
}
protected override void OnEvent(EventArgs e)
{
// Do something before the event
base.OnEvent(e);
// Do something after the event
}
}
There are a few things you can add/change in a derived class
Add a OnBeforeEvent / OnAfterEvent addition.
Choose not to broadcast the event (by conditionally not calling base.OnEvent(e)).
Vary the event args in some way.
Additionally, If you think about the way something like a page model works, it typically fires a Load event to notify when the page is loaded. Without a protected OnLoad method, derived classes would have to subscribe to it's own Load event to perform some action on load
public class MyPage : Page
{
protected override void OnLoad(EventArgs e)
{
// do something when the page is loaded
base.OnLoad(e);
}
}
versus:
public class MyPage : Page
{
public MyPage() : base()
{
this.Load += (sender,e) => {
// bleugh - subscribing to my own events
}
}
}
A good example might be the Paint event in Windows Forms.
// in MyButton : BaseButton : Control
void override OnPaint(object s, PaintEveargs e)
{
base.OnPaint(s, e); // Control: Draw background, BaseButton: draw border
// draw my own stuff
}
A button has several layers of base class, each drawing on top of each other.
I have a class, EventContainer.cs, which contains an event, say:
public event EventHandler AfterSearch;
I have another class, EventRaiser.cs. How do I raise (and not handle) the above said event from this class?
The raised event will in turn call the handler of the event in the EventContainer class. Something like this (this is obviously not correct):
EventContainer obj = new EventContainer();
RaiseEvent(obj.AfterSearch);
This is not possible, Events can only be risen from inside the class. If you could do that, it would defeat the purpose of events (being able to rise status changes from inside the class). I think you are misunderstanding the function of events - an event is defined inside a class and others can subscribe to it by doing
obj.AfterSearch += handler; (where handler is a method according to the signature of AfterSearch). One is able to subscribe to the event from the outside just fine, but it can only be risen from inside the class defining it.
It is POSSIBLE, but using clever hack.
Inspired by http://netpl.blogspot.com/2010/10/is-net-type-safe.html
If you don't believe, try this code.
using System;
using System.Runtime.InteropServices;
namespace Overlapping
{
[StructLayout(LayoutKind.Explicit)]
public class OverlapEvents
{
[FieldOffset(0)]
public Foo Source;
[FieldOffset(0)]
public OtherFoo Target;
}
public class Foo
{
public event EventHandler Clicked;
public override string ToString()
{
return "Hello Foo";
}
public void Click()
{
InvokeClicked(EventArgs.Empty);
}
private void InvokeClicked(EventArgs e)
{
var handler = Clicked;
if (handler != null)
handler(this, e);
}
}
public class OtherFoo
{
public event EventHandler Clicked;
public override string ToString()
{
return "Hello OtherFoo";
}
public void Click2()
{
InvokeClicked(EventArgs.Empty);
}
private void InvokeClicked(EventArgs e)
{
var handler = Clicked;
if (handler != null)
handler(this, e);
}
public void Clean()
{
Clicked = null;
}
}
class Test
{
public static void Test3()
{
var a = new Foo();
a.Clicked += AClicked;
a.Click();
var o = new OverlapEvents { Source = a };
o.Target.Click2();
o.Target.Clean();
o.Target.Click2();
a.Click();
}
static void AClicked(object sender, EventArgs e)
{
Console.WriteLine(sender.ToString());
}
}
}
You can write a public method on the class you want the event to fire from and fire the event when it is called. You can then call this method from whatever user of your class.
Of course, this ruins encapsulation and is bad design.
It looks like you're using the Delegate pattern. In this case, the AfterSearch event should be defined on the EventRaiser class, and the EventContainer class should consume the event:
In EventRaiser.cs
public event EventHandler BeforeSearch;
public event EventHandler AfterSearch;
public void ExecuteSearch(...)
{
if (this.BeforeSearch != null)
this.BeforeSearch();
// Do search
if (this.AfterSearch != null)
this.AfterSearch();
}
In EventContainer.cs
public EventContainer(...)
{
EventRaiser er = new EventRaiser();
er.AfterSearch += this.OnAfterSearch;
}
public void OnAfterSearch()
{
// Handle AfterSearch event
}
I stumbled across this problem as well, because i was experimenting with calling PropertyChanged events from outside. So you dont have to implement everything in every class. The solution from halorty wouldn't work using interfaces.
I found a solution working using heavy reflection. It is surely slow and is breaking the principle that events should only be called from inside a class. But it is interesting to find a generic solution to this problem....
It works because every event is a list of invocation methods being called.
So we can get the invocation list and call every listener attached to that event by our own.
Here you go....
class Program
{
static void Main(string[] args)
{
var instance = new TestPropertyChanged();
instance.PropertyChanged += PropertyChanged;
instance.RaiseEvent(nameof(INotifyPropertyChanged.PropertyChanged), new PropertyChangedEventArgs("Hi There from anywhere"));
Console.ReadLine();
}
private static void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
}
public static class PropertyRaiser
{
private static readonly BindingFlags staticFlags = BindingFlags.Instance | BindingFlags.NonPublic;
public static void RaiseEvent(this object instance, string eventName, EventArgs e)
{
var type = instance.GetType();
var eventField = type.GetField(eventName, staticFlags);
if (eventField == null)
throw new Exception($"Event with name {eventName} could not be found.");
var multicastDelegate = eventField.GetValue(instance) as MulticastDelegate;
if (multicastDelegate == null)
return;
var invocationList = multicastDelegate.GetInvocationList();
foreach (var invocationMethod in invocationList)
invocationMethod.DynamicInvoke(new[] {instance, e});
}
}
public class TestPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
There is good way to do this. Every event in C# has a delegate that specifies the sign of methods for that event. Define a field in your external class with type of your event delegate. get the the reference of that field in the constructor of external class and save it. In main class of your event, send the reference of event for delegate of external class. Now you can easily call the delegate in your external class.
public delegate void MyEventHandler(object Sender, EventArgs Args);
public class MyMain
{
public event MyEventHandler MyEvent;
...
new MyExternal(this.MyEvent);
...
}
public MyExternal
{
private MyEventHandler MyEvent;
public MyExternal(MyEventHandler MyEvent)
{
this.MyEvent = MyEvent;
}
...
this.MyEvent(..., ...);
...
}
Agree with Femaref -- and note this is an important difference between delegates and events (see for example this blog entry for an good discussion of this and other differences).
Depending on what you want to achieve, you might be better off with a delegate.
Not a good programming but if you want to do that any way you can do something like this
class Program
{
static void Main(string[] args)
{
Extension ext = new Extension();
ext.MyEvent += ext_MyEvent;
ext.Dosomething();
}
static void ext_MyEvent(int num)
{
Console.WriteLine(num);
}
}
public class Extension
{
public delegate void MyEventHandler(int num);
public event MyEventHandler MyEvent;
public void Dosomething()
{
int no = 0;
while(true){
if(MyEvent!=null){
MyEvent(++no);
}
}
}
}
I had a similar confusion and honestly find the answers here to be confusing. Although a couple hinted at solutions that I would later find would work.
My solution was to hit the books and become more familiar with delegates and event handlers.
Although I've used both for many years, I was never intimately familiar with them.
http://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices
gives the best explanation of both delegates and event handlers that I've ever read and clearly explains that a class can be a publisher of events and have other classes consume them.
This article: http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single discusses how to single-cast events to only one handler since delegates are multicast by definition . A delegate inherits system.MulticastDelegate most including the system delegates are Multicast.
I found that multicast meant that any event handler with the same signature would receive the raised event. Multicast behavior has caused me some sleepless nights as I stepped through code and saw my event seemingly erroneously being sent to handlers that I had no intention of getting this event. Both articles explains this behavior.
The second article shows you one way, and the first article shows you another, by making the delegate and the signature tightly typed.
I personally believe strong typing prevents stupid bugs that can be a pain to find. So I'd vote for the first article, even though I got the second article code working. I was just curious. :-)
I also got curious if I could get #2 articles code to behave like how I interpreted the original question above. Regardless of your chosen approach or if I'm also misinterpreting the original question, my real message is that I still think you would benefit from reading the first article as I did, especially if the questions or answers on this page leave you confused. If you are having multicast nightmares and need a quick solution then article 2 may help you.
I started playing with the second article's eventRaiser class. I made a simple windows form project.
I added the second articles class EventRaiser.cs to my project.
In the Main form's code, I defined a reference to that EventRaiser class at the top as
private EventRaiser eventRaiser = new EventRaiser();
I added a method in the main form code that I wanted to be called when the event was fired
protected void MainResponse( object sender, EventArgs eArgs )
{
MessageBox.Show("got to MainResponse");
}
then in the main form's constructor I added the event assignment:
eventRaiser.OnRaiseEvent += new EventHandler(MainResponse);`
I then created a class that would be instantiated by my main form called "SimpleClass" for lack of creative ingenuity at the moment.
Then I added a button and in the button's click event
I instantiated the SimpleClass code I wanted to raise an event from:
private void button1_Click( object sender, EventArgs e )
{
SimpleClass sc = new SimpleClass(eventRaiser);
}
Note the instance of "eventRaiser" that I passed to SimpleClass.cs. That was defined and instantiated earlier in the Main form code.
In the SimpleClass:
using System.Windows.Forms;
using SinglecastEvent; // see SingleCastEvent Project for info or http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single
namespace GenericTest
{
public class SimpleClass
{
private EventRaiser eventRaiser = new EventRaiser();
public SimpleClass( EventRaiser ev )
{
eventRaiser = ev;
simpleMethod();
}
private void simpleMethod()
{
MessageBox.Show("in FileWatcher.simple() about to raise the event");
eventRaiser.RaiseEvent();
}
}
}
The only point to the private method I called SimpleMethod was to verify that a privately scoped method could still raise the event, not that I doubted it, but I like to be positive.
I ran the project and this resulted in raising the event from the "simpleMethod" of the "SimpleClass" up to the main form and going to the expected correct method called MainResponse proving that one class can indeed raise an event that is consumed by a different class.
Yes the event has to be raised from within the class that needs it's change broadcast to other classes that care. Receiving classes can be one class or many many classes depending on how strongly typed you defined them or by making them single cast as in 2nd article.
Hope this helps and not muddy the water. Personally I've got a lot of delegates and events to clean up! Multicast demons begone!
The raising class has to get a fresh copy of the EventHandler.
One possible solution below.
using System;
namespace ConsoleApplication1
{
class Program
{
class HasEvent
{
public event EventHandler OnEnvent;
EventInvoker myInvoker;
public HasEvent()
{
myInvoker = new EventInvoker(this, () => OnEnvent);
}
public void MyInvokerRaising() {
myInvoker.Raise();
}
}
class EventInvoker
{
private Func<EventHandler> GetEventHandler;
private object sender;
public EventInvoker(object sender, Func<EventHandler> GetEventHandler)
{
this.sender = sender;
this.GetEventHandler = GetEventHandler;
}
public void Raise()
{
if(null != GetEventHandler())
{
GetEventHandler()(sender, new EventArgs());
}
}
}
static void Main(string[] args)
{
HasEvent h = new HasEvent();
h.OnEnvent += H_OnEnvent;
h.MyInvokerRaising();
}
private static void H_OnEnvent(object sender, EventArgs e)
{
Console.WriteLine("FIRED");
}
}
}
Use public EventHandler AfterSearch;
not
public event EventHandler AfterSearch;
Use a Delegate (an Action or Func) instead of an event. An event is essentially a delegate that can only be triggered from within the class.
I took a slightly different approach in solving this problem. My solution consisted of a winform front end, a main Class Library (DLL) and within that dll, a secondary working class:
WinForm
|------> PickGen Library
|---------> Allocations class
What I decided to do is to create events in the main dll (PickGen) that the Allocations class could call, then those event methods would called the events within the UI.
So, allocations raises an event in PickGen which takes the parameter values and raises the event in the form. From a code standpoint, this is in the lowest class:
public delegate void AllocationService_RaiseAllocLog(string orderNumber, string message, bool logToDatabase);
public delegate void AllocationService_RaiseAllocErrorLog(string orderNumber, string message, bool logToDatabase);
public class AllocationService { ...
public event AllocationService_RaiseAllocLog RaiseAllocLog;
public event AllocationService_RaiseAllocErrorLog RaiseAllocErrorLog;
then later in the subclass code:
RaiseAllocErrorLog(SOHNUM_0, ShipmentGenerated + ": Allocated line QTY was: " + allocatedline.QTY_0 + ", Delivered was: " + QTY_0 + ". Problem batch.", false);
In the main DLL Class library I have these two event methods:
private void PickGenLibrary_RaiseAllocLog(string orderNumber, string message, bool updateDB)
{
RaiseLog(orderNumber, message, false);
}
private void PickGenLibrary_RaiseAllocErrorLog(string orderNumber, string message, bool updateDB)
{
RaiseErrorLog(orderNumber, message, false);
}
and I make the connection here when I create the allocation object:
AllocationService allsvc = new AllocationService(PickResult);
allsvc.RaiseAllocLog += new AllocationService_RaiseAllocLog(PickGenLibrary_RaiseAllocLog);
allsvc.RaiseAllocErrorLog += new AllocationService_RaiseAllocErrorLog(PickGenLibrary_RaiseAllocErrorLog);
and I also then have delegates that are set up to tie the main class with the winform code:
public delegate void JPPAPickGenLibrary_RaiseLog(string orderNumber, string message, bool logToDatabase);
public delegate void JPPAPickGenLibrary_RaiseErrorLog(string orderNumber, string message, bool logToDatabase);
It may not be the most elegant way to do it, but in the end, it does work and without being too obscure.
A nested class with an instance of the outer class provided in the constructor can access even private members of the outer class. As explained more here: stackoverflow question on inner classes.
This includes the ability to raise events in the outer class. This EventRaisers class could be internal, or otherwise controlled somehow, because it could technically otherwise be created by any script with a reference to the outer class instance.
Very simple example. i like to do it this way using EventHandler.
class Program
{
static void Main(string[] args)
{
MyExtension ext = new MyExtension();
ext.MyEvent += ext_MyEvent;
ext.Dosomething();
Console.ReadLine();
}
static void ext_MyEvent(object sender, int num)
{
Console.WriteLine("Event fired.... "+num);
}
}
public class MyExtension
{
public event EventHandler<int> MyEvent;
public void Dosomething()
{
int no = 1;
if (MyEvent != null)
MyEvent(this, ++no);
}
}
}
I wanted to ask if this is Event possible in C#. I have not much worked with Events till now.
Say I have a class A which subscribed to a FormClosing Event of a form:
public class A
{
private void f_FormClosed(object sender, FormClosedEventArgs e)
{
//Now here a public Event should be called
}
}
Now there I want a public Event to be called. Let's say now I have another class B which has a certain method.
public class B
{
public void DoSomething()
{
}
}
Now what I want to do:
A Form gets closed so class A is getting notified. There, a public Event gets triggered (which is somewhere in a public class). I want to subscribe my method in class B to this Event so it gets called when that happens. Is this possible? And how is the syntax? I haven't found something useful till now.
Edit: I can't create an instance of class B directly from class A.
Its possible .
Create a new event in A.
Raise the event within the eventhandler f_FormClosed
Subscribe to this event in B.
Within the eventhandler in B call the method DoSomething
For the syntax part you could check MSDN
// A delegate type for hooking up change notifications.
public delegate void ChangedEventHandler(object sender, EventArgs e);
// A class that works just like ArrayList, but sends event
// notifications whenever the list changes.
public class ListWithChangedEvent: ArrayList
{
// An event that clients can use to be notified whenever the
// elements of the list change.
public event ChangedEventHandler Changed;
// Invoke the Changed event; called whenever list changes
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
//you raise the event here.
Changed(this, e);
}
}
Now in your other class do something like this
class EventListener
{
private ListWithChangedEvent List;
public EventListener(ListWithChangedEvent list)
{
List = list;
// Add "ListChanged" to the Changed event on "List".
//This is how we subscribe to the event created in ListWithChangedEvent class
List.Changed += new ChangedEventHandler(ListChanged);
}
// This will be called whenever the list changes.
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
}
I want the following:
public static class A
{
//do something
UpdateListOnMainForm()
}
I already know I can do this by Events, I tried to create a Event but I don't need any parameters, I just want if class A calls the Event the ListView is updating its data.
Can someone help me creating this Event? Or maybe there is a better way?
Thank you in advance!
Edit: At least I'm trying to update a ListView when I've changed the list (other class) from which the ListView takes its data from.
You want a static class to be able to trigger your main form to update its list view via an event without specifically knowing about the main form, as I understand it. This is how I would do that:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SingletonA.GetInstance.MyEvent += UpdateListView;
}
private void UpdateListView(object sender, EventArgs e)
{
// Update your listview
}
}
//lazy initialization of singleton - not thread safe see http://www.dotnettricks.com/learn/designpatterns/singleton-design-pattern-dotnet for other thread safe version
public class SingletonA
{
private static SingletonA instance = null;
private SingletonA() { }
public event EventHandler<EventArgs> MyEvent;
void TellFormToUpdateListView()
{
MyEvent?.Invoke(typeof(SingletonA), EventArgs.Empty);
}
public static SingletonA GetInstance
{
get
{
if (instance == null)
instance = new SingletonA();
return instance;
}
}
}
I have a class, EventContainer.cs, which contains an event, say:
public event EventHandler AfterSearch;
I have another class, EventRaiser.cs. How do I raise (and not handle) the above said event from this class?
The raised event will in turn call the handler of the event in the EventContainer class. Something like this (this is obviously not correct):
EventContainer obj = new EventContainer();
RaiseEvent(obj.AfterSearch);
This is not possible, Events can only be risen from inside the class. If you could do that, it would defeat the purpose of events (being able to rise status changes from inside the class). I think you are misunderstanding the function of events - an event is defined inside a class and others can subscribe to it by doing
obj.AfterSearch += handler; (where handler is a method according to the signature of AfterSearch). One is able to subscribe to the event from the outside just fine, but it can only be risen from inside the class defining it.
It is POSSIBLE, but using clever hack.
Inspired by http://netpl.blogspot.com/2010/10/is-net-type-safe.html
If you don't believe, try this code.
using System;
using System.Runtime.InteropServices;
namespace Overlapping
{
[StructLayout(LayoutKind.Explicit)]
public class OverlapEvents
{
[FieldOffset(0)]
public Foo Source;
[FieldOffset(0)]
public OtherFoo Target;
}
public class Foo
{
public event EventHandler Clicked;
public override string ToString()
{
return "Hello Foo";
}
public void Click()
{
InvokeClicked(EventArgs.Empty);
}
private void InvokeClicked(EventArgs e)
{
var handler = Clicked;
if (handler != null)
handler(this, e);
}
}
public class OtherFoo
{
public event EventHandler Clicked;
public override string ToString()
{
return "Hello OtherFoo";
}
public void Click2()
{
InvokeClicked(EventArgs.Empty);
}
private void InvokeClicked(EventArgs e)
{
var handler = Clicked;
if (handler != null)
handler(this, e);
}
public void Clean()
{
Clicked = null;
}
}
class Test
{
public static void Test3()
{
var a = new Foo();
a.Clicked += AClicked;
a.Click();
var o = new OverlapEvents { Source = a };
o.Target.Click2();
o.Target.Clean();
o.Target.Click2();
a.Click();
}
static void AClicked(object sender, EventArgs e)
{
Console.WriteLine(sender.ToString());
}
}
}
You can write a public method on the class you want the event to fire from and fire the event when it is called. You can then call this method from whatever user of your class.
Of course, this ruins encapsulation and is bad design.
It looks like you're using the Delegate pattern. In this case, the AfterSearch event should be defined on the EventRaiser class, and the EventContainer class should consume the event:
In EventRaiser.cs
public event EventHandler BeforeSearch;
public event EventHandler AfterSearch;
public void ExecuteSearch(...)
{
if (this.BeforeSearch != null)
this.BeforeSearch();
// Do search
if (this.AfterSearch != null)
this.AfterSearch();
}
In EventContainer.cs
public EventContainer(...)
{
EventRaiser er = new EventRaiser();
er.AfterSearch += this.OnAfterSearch;
}
public void OnAfterSearch()
{
// Handle AfterSearch event
}
I stumbled across this problem as well, because i was experimenting with calling PropertyChanged events from outside. So you dont have to implement everything in every class. The solution from halorty wouldn't work using interfaces.
I found a solution working using heavy reflection. It is surely slow and is breaking the principle that events should only be called from inside a class. But it is interesting to find a generic solution to this problem....
It works because every event is a list of invocation methods being called.
So we can get the invocation list and call every listener attached to that event by our own.
Here you go....
class Program
{
static void Main(string[] args)
{
var instance = new TestPropertyChanged();
instance.PropertyChanged += PropertyChanged;
instance.RaiseEvent(nameof(INotifyPropertyChanged.PropertyChanged), new PropertyChangedEventArgs("Hi There from anywhere"));
Console.ReadLine();
}
private static void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
}
public static class PropertyRaiser
{
private static readonly BindingFlags staticFlags = BindingFlags.Instance | BindingFlags.NonPublic;
public static void RaiseEvent(this object instance, string eventName, EventArgs e)
{
var type = instance.GetType();
var eventField = type.GetField(eventName, staticFlags);
if (eventField == null)
throw new Exception($"Event with name {eventName} could not be found.");
var multicastDelegate = eventField.GetValue(instance) as MulticastDelegate;
if (multicastDelegate == null)
return;
var invocationList = multicastDelegate.GetInvocationList();
foreach (var invocationMethod in invocationList)
invocationMethod.DynamicInvoke(new[] {instance, e});
}
}
public class TestPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
There is good way to do this. Every event in C# has a delegate that specifies the sign of methods for that event. Define a field in your external class with type of your event delegate. get the the reference of that field in the constructor of external class and save it. In main class of your event, send the reference of event for delegate of external class. Now you can easily call the delegate in your external class.
public delegate void MyEventHandler(object Sender, EventArgs Args);
public class MyMain
{
public event MyEventHandler MyEvent;
...
new MyExternal(this.MyEvent);
...
}
public MyExternal
{
private MyEventHandler MyEvent;
public MyExternal(MyEventHandler MyEvent)
{
this.MyEvent = MyEvent;
}
...
this.MyEvent(..., ...);
...
}
Agree with Femaref -- and note this is an important difference between delegates and events (see for example this blog entry for an good discussion of this and other differences).
Depending on what you want to achieve, you might be better off with a delegate.
Not a good programming but if you want to do that any way you can do something like this
class Program
{
static void Main(string[] args)
{
Extension ext = new Extension();
ext.MyEvent += ext_MyEvent;
ext.Dosomething();
}
static void ext_MyEvent(int num)
{
Console.WriteLine(num);
}
}
public class Extension
{
public delegate void MyEventHandler(int num);
public event MyEventHandler MyEvent;
public void Dosomething()
{
int no = 0;
while(true){
if(MyEvent!=null){
MyEvent(++no);
}
}
}
}
I had a similar confusion and honestly find the answers here to be confusing. Although a couple hinted at solutions that I would later find would work.
My solution was to hit the books and become more familiar with delegates and event handlers.
Although I've used both for many years, I was never intimately familiar with them.
http://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices
gives the best explanation of both delegates and event handlers that I've ever read and clearly explains that a class can be a publisher of events and have other classes consume them.
This article: http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single discusses how to single-cast events to only one handler since delegates are multicast by definition . A delegate inherits system.MulticastDelegate most including the system delegates are Multicast.
I found that multicast meant that any event handler with the same signature would receive the raised event. Multicast behavior has caused me some sleepless nights as I stepped through code and saw my event seemingly erroneously being sent to handlers that I had no intention of getting this event. Both articles explains this behavior.
The second article shows you one way, and the first article shows you another, by making the delegate and the signature tightly typed.
I personally believe strong typing prevents stupid bugs that can be a pain to find. So I'd vote for the first article, even though I got the second article code working. I was just curious. :-)
I also got curious if I could get #2 articles code to behave like how I interpreted the original question above. Regardless of your chosen approach or if I'm also misinterpreting the original question, my real message is that I still think you would benefit from reading the first article as I did, especially if the questions or answers on this page leave you confused. If you are having multicast nightmares and need a quick solution then article 2 may help you.
I started playing with the second article's eventRaiser class. I made a simple windows form project.
I added the second articles class EventRaiser.cs to my project.
In the Main form's code, I defined a reference to that EventRaiser class at the top as
private EventRaiser eventRaiser = new EventRaiser();
I added a method in the main form code that I wanted to be called when the event was fired
protected void MainResponse( object sender, EventArgs eArgs )
{
MessageBox.Show("got to MainResponse");
}
then in the main form's constructor I added the event assignment:
eventRaiser.OnRaiseEvent += new EventHandler(MainResponse);`
I then created a class that would be instantiated by my main form called "SimpleClass" for lack of creative ingenuity at the moment.
Then I added a button and in the button's click event
I instantiated the SimpleClass code I wanted to raise an event from:
private void button1_Click( object sender, EventArgs e )
{
SimpleClass sc = new SimpleClass(eventRaiser);
}
Note the instance of "eventRaiser" that I passed to SimpleClass.cs. That was defined and instantiated earlier in the Main form code.
In the SimpleClass:
using System.Windows.Forms;
using SinglecastEvent; // see SingleCastEvent Project for info or http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single
namespace GenericTest
{
public class SimpleClass
{
private EventRaiser eventRaiser = new EventRaiser();
public SimpleClass( EventRaiser ev )
{
eventRaiser = ev;
simpleMethod();
}
private void simpleMethod()
{
MessageBox.Show("in FileWatcher.simple() about to raise the event");
eventRaiser.RaiseEvent();
}
}
}
The only point to the private method I called SimpleMethod was to verify that a privately scoped method could still raise the event, not that I doubted it, but I like to be positive.
I ran the project and this resulted in raising the event from the "simpleMethod" of the "SimpleClass" up to the main form and going to the expected correct method called MainResponse proving that one class can indeed raise an event that is consumed by a different class.
Yes the event has to be raised from within the class that needs it's change broadcast to other classes that care. Receiving classes can be one class or many many classes depending on how strongly typed you defined them or by making them single cast as in 2nd article.
Hope this helps and not muddy the water. Personally I've got a lot of delegates and events to clean up! Multicast demons begone!
The raising class has to get a fresh copy of the EventHandler.
One possible solution below.
using System;
namespace ConsoleApplication1
{
class Program
{
class HasEvent
{
public event EventHandler OnEnvent;
EventInvoker myInvoker;
public HasEvent()
{
myInvoker = new EventInvoker(this, () => OnEnvent);
}
public void MyInvokerRaising() {
myInvoker.Raise();
}
}
class EventInvoker
{
private Func<EventHandler> GetEventHandler;
private object sender;
public EventInvoker(object sender, Func<EventHandler> GetEventHandler)
{
this.sender = sender;
this.GetEventHandler = GetEventHandler;
}
public void Raise()
{
if(null != GetEventHandler())
{
GetEventHandler()(sender, new EventArgs());
}
}
}
static void Main(string[] args)
{
HasEvent h = new HasEvent();
h.OnEnvent += H_OnEnvent;
h.MyInvokerRaising();
}
private static void H_OnEnvent(object sender, EventArgs e)
{
Console.WriteLine("FIRED");
}
}
}
Use public EventHandler AfterSearch;
not
public event EventHandler AfterSearch;
Use a Delegate (an Action or Func) instead of an event. An event is essentially a delegate that can only be triggered from within the class.
I took a slightly different approach in solving this problem. My solution consisted of a winform front end, a main Class Library (DLL) and within that dll, a secondary working class:
WinForm
|------> PickGen Library
|---------> Allocations class
What I decided to do is to create events in the main dll (PickGen) that the Allocations class could call, then those event methods would called the events within the UI.
So, allocations raises an event in PickGen which takes the parameter values and raises the event in the form. From a code standpoint, this is in the lowest class:
public delegate void AllocationService_RaiseAllocLog(string orderNumber, string message, bool logToDatabase);
public delegate void AllocationService_RaiseAllocErrorLog(string orderNumber, string message, bool logToDatabase);
public class AllocationService { ...
public event AllocationService_RaiseAllocLog RaiseAllocLog;
public event AllocationService_RaiseAllocErrorLog RaiseAllocErrorLog;
then later in the subclass code:
RaiseAllocErrorLog(SOHNUM_0, ShipmentGenerated + ": Allocated line QTY was: " + allocatedline.QTY_0 + ", Delivered was: " + QTY_0 + ". Problem batch.", false);
In the main DLL Class library I have these two event methods:
private void PickGenLibrary_RaiseAllocLog(string orderNumber, string message, bool updateDB)
{
RaiseLog(orderNumber, message, false);
}
private void PickGenLibrary_RaiseAllocErrorLog(string orderNumber, string message, bool updateDB)
{
RaiseErrorLog(orderNumber, message, false);
}
and I make the connection here when I create the allocation object:
AllocationService allsvc = new AllocationService(PickResult);
allsvc.RaiseAllocLog += new AllocationService_RaiseAllocLog(PickGenLibrary_RaiseAllocLog);
allsvc.RaiseAllocErrorLog += new AllocationService_RaiseAllocErrorLog(PickGenLibrary_RaiseAllocErrorLog);
and I also then have delegates that are set up to tie the main class with the winform code:
public delegate void JPPAPickGenLibrary_RaiseLog(string orderNumber, string message, bool logToDatabase);
public delegate void JPPAPickGenLibrary_RaiseErrorLog(string orderNumber, string message, bool logToDatabase);
It may not be the most elegant way to do it, but in the end, it does work and without being too obscure.
A nested class with an instance of the outer class provided in the constructor can access even private members of the outer class. As explained more here: stackoverflow question on inner classes.
This includes the ability to raise events in the outer class. This EventRaisers class could be internal, or otherwise controlled somehow, because it could technically otherwise be created by any script with a reference to the outer class instance.
Very simple example. i like to do it this way using EventHandler.
class Program
{
static void Main(string[] args)
{
MyExtension ext = new MyExtension();
ext.MyEvent += ext_MyEvent;
ext.Dosomething();
Console.ReadLine();
}
static void ext_MyEvent(object sender, int num)
{
Console.WriteLine("Event fired.... "+num);
}
}
public class MyExtension
{
public event EventHandler<int> MyEvent;
public void Dosomething()
{
int no = 1;
if (MyEvent != null)
MyEvent(this, ++no);
}
}
}