43

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);
Jonathan Nixon
  • 4,940
  • 5
  • 39
  • 53
samar
  • 5,021
  • 9
  • 47
  • 71
  • 2
    This old post raises an issue that can easily be circumvented using an event hub like [TinyMessenger](https://github.com/grumpydev/TinyMessenger). – Larry Apr 07 '17 at 09:49

14 Answers14

46

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.

Femaref
  • 60,705
  • 7
  • 138
  • 176
  • Thanks Femaref for the insider. Will try to achieve in some other way. – samar Dec 09 '10 at 05:28
  • 4
    @Femaref: It is **POSSIBLE**, try my answer below. – halorty Oct 05 '11 at 11:01
  • 2
    Does it have practicability? No, it doesn't, your first line says it, it's a hack, if I'd find that in source code somewhere, I'd slowly close the window and calmly work away, never to be seen again. – Femaref Oct 05 '11 at 11:12
  • 3
    @Femaref: I didn't say it is practicability, I said "It is possible", because you said "This is not possible, Events can only be risen from inside the class" - but it is not true. – halorty Oct 05 '11 at 11:21
  • @Femaref Just as a side note... When doing this: 'combo.selectedvalue = "whatever"'. Then the SelectionChanged event on the combo is not raised. Seems like a bug to me. In that case it would be quite handy to raise an event from otside the combo class. (And sorry for the late-post) – Noel Widmer Oct 21 '14 at 09:28
  • Actually it's very easy to raise events from outside the class- but that class needs to pass you the event handler to do it. I regularly use an extension method (VERY simplified): `static void Raise(this EventHandler ev) => if (ev != null) ev.BeginInvoke(..);`, It's the extension method that raises the event. What seems to be 'impossible' is getting the `EventHandler` from another class. – Flynn1179 Apr 20 '17 at 14:33
  • I came to this question when tried to change the border color of hovered button. As I know, there are no winforms native tools to realize it. I created methods like `{void ButtonCustomHover(object sender, EventArgs e) { Button hoveredButton = sender as Button; hoveredButton.FlatAppearance.BorderColor = System.Drawing.Color.Orange; }`. It is in window class now, but I want to extract it to `UiExttectionClass` and use in other classes. Will you say that I go by wrong way? – Takeshi Tokugawa YD Oct 25 '17 at 08:46
31

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());
        }
    }
}
halorty
  • 644
  • 1
  • 7
  • 11
  • Really awesome! Never saw 'this' in a class be of any other type than the type of the class it is used in. – Mike de Klerk Apr 07 '17 at 06:28
  • 4
    This does not answer the question. You are not raising the event from outside the class, you are calling a function that raises the event from within the class, which is what the answer above said. If all you need is to notify another class, you should use the mediator pattern (which this solution is loosely based on). – AsPas Feb 01 '21 at 09:35
17

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • 2
    I definitely agree with you that it would be bad design, but the pattern that I often use is having a `public static class` called `GlobalEvents` which you can call upon to deliver global events that _any_ class may listen to. The only way to currently do this is to use `System.Action`. The problem arises when I want to have named arguments. Currently there's an XML summary tag that you can populate with the arguments, but it still isn't quite as readable and not natively supported in most IDEs' auto-summary functionality. – Addison Yarborough Mar 04 '19 at 00:14
8

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
}
Mark Pim
  • 9,898
  • 7
  • 40
  • 59
6

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;
}
  • That's what I call a solution!!! I did some research and you can also invoke it without `DynamicInvoke` like that `subscriber.Method.Invoke(subscriber.Target, new object?[] { source, new PropertyChangedEventArgs(propertyName) });` where `subscriber` here is your `invocationMethod`. – t3chb0t Mar 01 '23 at 19:08
4

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(..., ...);
     ...
}
  • I can't make head or tail of this answer; the ...'s stop it working and hide too much, the reuse of the MyEvent variable name doesn't help and I don't think it solves the problem. – Stephen Turner May 09 '13 at 11:35
  • @webturner: He's declaring an event in one class, then passing it to another class during instantiation and calling the event from the new class. It's hackish, to be sure, but I believe it would work. –  Aug 22 '14 at 03:21
  • 2
    This would not work. The Eventhandler gets copied and all subscribers that subscriebed after the copy would not get notified. – Johannes Jun 21 '16 at 00:00
  • 1
    Linking @Johannes more correct answer here: https://stackoverflow.com/a/37933566/661933. Demo: https://dotnetfiddle.net/sCrZzu – nawfal Nov 13 '20 at 06:29
3

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.

Tim Barrass
  • 4,813
  • 2
  • 29
  • 55
3

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);
            }
        }
    }
}
Jagdish
  • 129
  • 1
  • 8
  • 1
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – thewaywewere Jun 28 '17 at 05:55
2

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!

user3866622
  • 189
  • 1
  • 2
2

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");
        }
    }
}
Johannes
  • 6,490
  • 10
  • 59
  • 108
  • Why pass the event as `Func GetEventHandler` ? you can directly pass `EvetnHandler` from the parent class. – nawfal Nov 12 '20 at 21:36
  • 1
    I think the underlying implementation is a value type so you need to obtain a fresh copy just before using it. Otherwise you would not reach any observers that were registering to the event after construction time. – Johannes Nov 12 '20 at 22:46
  • Makes sense, I totally overlooked it. – nawfal Nov 13 '20 at 04:09
2

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.

CBFT
  • 273
  • 2
  • 14
0

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.

j.hull
  • 321
  • 3
  • 15
0

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.

Doug H
  • 1
-3

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);
        }
    }
}
Mist
  • 684
  • 9
  • 30