I found a solution on the MSDN forums. The sample code below will remove all Click
events from button1
.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += button1_Click;
button1.Click += button1_Click2;
button2.Click += button2_Click;
}
private void button1_Click(object sender, EventArgs e) => MessageBox.Show("Hello");
private void button1_Click2(object sender, EventArgs e) => MessageBox.Show("World");
private void button2_Click(object sender, EventArgs e) => RemoveClickEvent(button1);
private void RemoveClickEvent(Button b)
{
FieldInfo f1 = typeof(Control).GetField("EventClick",
BindingFlags.Static | BindingFlags.NonPublic);
object obj = f1.GetValue(b);
PropertyInfo pi = b.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
list.RemoveHandler(obj, list[obj]);
}
}
To understand event handlers, you need to understand delegates. In C#, you can think of a delegate as a pointer (or a reference) to a method. This is useful because the pointer can be passed around as a value.
The central concept of a delegate is its signature, or shape. That is (1) the return type and (2) the input arguments. For example, if we create a delegate void MyDelegate(object sender, EventArgs e)
, it can only point to methods which return void
, and take an object
and EventArgs
. Kind of like a square hole and a square peg. So we say these methods have the same signature, or shape, as the delegate.
So knowing how to create a reference to a method, let's think about the purpose of events: we want to cause some code to be executed when something happens elsewhere in the system - or "handle the event". To do this, we create specific methods for the code we want to be executed. The glue between the event and the methods to be executed are the delegates. The event must internally store a "list" of pointers to the methods to call when the event is raised.* Of course, to be able to call a method, we need to know what arguments to pass to it! We use the delegate as the "contract" between the event and all the specific methods that will be called.
So the default EventHandler
(and many like it) represents a specific shape of method (again, void/object-EventArgs). When you declare an event, you are saying which shape of method (EventHandler) that event will invoke, by specifying a delegate:
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);
//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;
//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
//Do some stuff
}
//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
//To raise the event within a method.
SomethingHappened("bar");
(*This is the key to events in .NET and peels away the "magic" - an event is really, under the covers, just a list of methods of the same "shape". The list is stored where the event lives. When the event is "raised", it's really just "go through this list of methods and call each one, using these values as the parameters". Assigning an event handler is just a prettier, easier way of adding your method to this list of methods to be called).
Best Answer
I implemented a solution to this that works in just about any version of Delphi - it was originally implemented in Delphi 7, although I haven't tested it in earlier versions (but if you're using Delphi 7 yourself, then that's all you need to know, right?). :)
iirc this was at least in part the inspiration for Allen Bauer's post. You can see my implementation demonstrated in some videos and download the code from my blog:
The posts you are interested in are tagged "multicast". The download link for the final code is available in this post.
In my approach, you derive a class from TMultiCastEvent. All your derived class has to do is implement some simple type safety protection for adding, removing and invoking an event with a specific signature.
An implementation for TNotifyEvent - procedure(Sender: TObject) - is provided with the implementation both "to get you going" (ime most "useful" multicast events are simple notifications) and also as an example of how to derive multicast event classes for specific event signatures.
Once you have your multicast event class you can use regular "event handlers" interchangeably with the multi-cast version, e.g. given some imaginary button class with a multi-cast On_Click event (I've adopted a convention of interposing an underscore in the event name to identify it as multicast, vs regular "uni-cast" events):
Code that assigns a handler to a unicast click event:
Can directly add that same handler to a multi-cast Notify event:
My implementation also includes a number of refinements, such as the ability to disable events and have handlers automatically removed from handlers when the implementing object is destroyed (this involves a small amount of housekeeping which can be ignored if necessary but which can be useful under certain circumstances).
All of which is described and demonstrated in my blog posts.
Enjoy. :)