Is there a way to list all fired events for specific WinForms controls without explicitly creating a handler for each possible event? For example, I might want to see the sequence of events that fire between a DataGridView and the BindingSource during various databinding actions.
You could use reflection, but it's going to be slightly tricky because of the various event handler signatures involved. Basically you'd have to get the EventInfo for each event in the type, and use the EventHandlerType property to work out what delegate type to create before calling AddEventHandler. Delegate.CreateDelegate works for everything that follows the normal event handler pattern though...
Here's a sample app. Note that it's not doing any checking - if you give it something with a "non-standard" event it will throw an exception. You could fairly easily use reflection to print out the event args too.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace ConsoleApp
{
class Program
{
[STAThread]
static void Main(string[] args)
{
Form form = new Form { Size = new Size(400, 200) };
Button button = new Button { Text = "Click me" };
form.Controls.Add(button);
EventSubscriber.SubscribeAll(button);
Application.Run(form);
}
}
class EventSubscriber
{
private static readonly MethodInfo HandleMethod =
typeof(EventSubscriber)
.GetMethod("HandleEvent",
BindingFlags.Instance |
BindingFlags.NonPublic);
private readonly EventInfo evt;
private EventSubscriber(EventInfo evt)
{
this.evt = evt;
}
private void HandleEvent(object sender, EventArgs args)
{
Console.WriteLine("Event {0} fired", evt.Name);
}
private void Subscribe(object target)
{
Delegate handler = Delegate.CreateDelegate(
evt.EventHandlerType, this, HandleMethod);
evt.AddEventHandler(target, handler);
}
public static void SubscribeAll(object target)
{
foreach (EventInfo evt in target.GetType().GetEvents())
{
EventSubscriber subscriber = new EventSubscriber(evt);
subscriber.Subscribe(target);
}
}
}
}
I think you could use Reflection to do this.
This cannot be done. If you use Reflector to view a lot of the Framework's classes you'll find a common pattern when firing events:
// fire event
if (EventDelegate != null)
EventDelegate(eventArgs);
So the event is not even fired if no one is subscribing to it
Related
The code I am writing is actually a WPF behaviour to get the selected items from a grid control (SelectedItems, as we know, is not a bindable property). I am actually using a Telerik RadGridView but I would like the Behaviour to be general for anything with a SelectionChanged event. However, different controls have different signatures for the SelectionChanged event handlers (RadGridView uses Telerik.Windows.Controls.SelectionChangeEventArgs whereas a standard GridView uses System.Windows.Controls.SelectionChangedEventArgs). The one thing we can be sure of is that the event args will be derived from EventArgs (in fact we can be sure that it will be derived from RoutedEventArgs).
However, while I can write a general event handler that takes a RoutedEventArgs as its second parameter, and I can use reflection to get the EventInfo for the SelectionChangedEvent, I can't hook the handler to the event without using the precise signature for the event handler - in this case the RadGridView handler.
Here is my code. I've included all of it but the important bit is SelectItemPropertyChanged, which is the DependencyObject PropertyChangedCallback that attempts to hook up the event handler SelectionChangedHandler to the SelectionChangedEvent. (The code in SelectionChangedHandler is irrelevant to the question but I've left it in so it's clear what I'm doing).
public static class SelectedItemsChangedBehaviour{
public static readonly DependencyProperty SelectItemsProperty =
DependencyProperty.RegisterAttached("SelectItems", typeof(bool), typeof(SelectedItemsChangedBehaviour),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(SelectItemPropertyChanged)));
public static void SetSelectItems(DependencyObject dependencyObject, bool selectItems)
{
dependencyObject.SetValue(SelectItemsProperty, selectItems);
}
public static bool GetSelectItems(DependencyObject dependencyObject)
{
return (bool)dependencyObject.GetValue(SelectItemsProperty);
}
private static void SelectItemPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
// No common base for all classes with SelectionChanged event so use reflection
EventInfo selectionChangedEventInfo = dependencyObject.GetType().GetEvent("SelectionChanged");
if (selectionChangedEventInfo == null)
{
throw new ArgumentException("Must have a SelectionChanged event.");
}
if ((bool)dependencyPropertyChangedEventArgs.OldValue)
{
// This is what I want to do, but it throws because the handler signature is wrong
selectionChangedEventInfo.RemoveEventHandler(dependencyObject, (RoutedEventHandler)SelectionChangedHandler);
// This works fine because it is of the right type for the RadGridView but is not general
//selectionChangedEventInfo.RemoveEventHandler(dependencyObject, (EventHandler<Telerik.Windows.Controls.SelectionChangeEventArgs>)SelectionChangedHandler);
}
if ((bool)dependencyPropertyChangedEventArgs.NewValue)
{
// This is what I want to do, but it throws because the handler signature is wrong
selectionChangedEventInfo.AddEventHandler(dependencyObject, (RoutedEventHandler)SelectionChangedHandler);
// This works fine because it is of the right type for the RadGridView but is not general
//selectionChangedEventInfo.AddEventHandler(dependencyObject, (EventHandler<Telerik.Windows.Controls.SelectionChangeEventArgs>)SelectionChangedHandler);
}
}
private static void SelectionChangedHandler(object sender, RoutedEventArgs eventArgs)
{
// No common base for all classes with AddedItems/RemovedItems (eg. System.Windows.Controls.SelectionChangedEventArgs / Telerik.Windows.Controls.SelectionChangeEventArgs
PropertyInfo addedItemsInfo = eventArgs.GetType().GetProperty("AddedItems");
PropertyInfo removedItemsInfo = eventArgs.GetType().GetProperty("RemovedItems");
if (addedItemsInfo == null || removedItemsInfo == null)
{
throw new ArgumentException("Must have AddedItems and RemovedItems");
}
foreach (object item in (IEnumerable)addedItemsInfo.GetValue(eventArgs, null))
{
((ISelectable)item).IsSelected = true;
}
foreach (object item in (IEnumerable)removedItemsInfo.GetValue(eventArgs, null))
{
((ISelectable)item).IsSelected = false;
}
}
I have tried all sorts of ways to use reflection to get the correct signature for the for the handler, and thereby create a delegate to the right type, but I just can't make it work - AddEventHandler (and RemoveEventHandler) throws an InvalidArgumentException, full stack trace as follows:
{"Object of type 'System.Windows.RoutedEventHandler' cannot be converted to type 'System.EventHandler`1[Telerik.Windows.Controls.SelectionChangeEventArgs]'."}
at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
Can anyone advise?
You need to convert delegate to event's EventHandlerType when calling AddEventHandler. Here's a sample app:
using System;
using System.Reflection;
using System.Threading;
namespace App
{
class Program
{
public event EventHandler<ThreadExceptionEventArgs> E;
static void Main ()
{
new Program().Run();
}
private void Run ()
{
EventInfo e = typeof(Program).GetEvent("E");
EventHandler untypedHandler = OnE;
Delegate typedHandler = Delegate.CreateDelegate(e.EventHandlerType,
untypedHandler.Target, untypedHandler.Method);
e.AddEventHandler(this, typedHandler);
E(this, new ThreadExceptionEventArgs(new Exception("Hello world!")));
}
private void OnE (object sender, EventArgs args)
{
Console.WriteLine(((ThreadExceptionEventArgs)args).Exception.Message);
}
}
}
I have this requirement where I need to show a custom popUp as a page overlay. This custom PopUp is random and can show up on any page (based on some logic). I have registered the OnBackKeyPress event (for the current page) in this custom PopUp class.
But because almost all the pages of app also has an OnBackKeyPress method defined (again as per business requirement), the new event registration (in the custom PopUp class) takes place after the previous one. So on pressing the hardware back key, the OnBackKeyPress method written in pages is getting called first followed by the OnBackKeyPress method written in custom PopUp class. I need to avoid calling OnBackKeyPress method written in pages.
Solutions not acceptable :
make the new popUp control as a PhoneApplicationPage (we need some transparency where we can show the current page's data)
Put check on OnBackKeyPress method on all pages for the popUp (so many pages in ap!)
Extend PhoneApplicationPage and write a new OnBackKeyPress method to handle the same (no no!)
Here is an example of how to subscribe an event before already subscribed handlers:
class EventTesterClass // Simple class that raise an event
{
public event EventHandler Func;
public void RaiseEvent()
{
Func(null, EventArgs.Empty);
}
}
static void subscribeEventBeforeOthers<InstanceType>(InstanceType instance, string eventName, EventHandler handler) // Magic method that unsubscribe subscribed methods, subscribe the new method and resubscribe previous methods
{
Type instanceType = typeof(InstanceType);
var eventInfo = instanceType.GetEvent(eventName);
var eventFieldInfo = instanceType.GetField(eventInfo.Name,
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.GetField);
var eventFieldValue = (System.Delegate)eventFieldInfo.GetValue(instance);
var methods = eventFieldValue.GetInvocationList();
foreach (var item in methods)
{
eventInfo.RemoveEventHandler(instance, item);
}
eventInfo.AddEventHandler(instance, handler);
foreach (var item in methods)
{
eventInfo.AddEventHandler(instance, item);
}
}
static void Main(string[] args)
{
var evntTest = new EventTesterClass();
evntTest.Func += handler_Func;
evntTest.Func += handler_Func1;
evntTest.RaiseEvent();
Console.WriteLine();
subscribeEventBeforeOthers(evntTest, "Func", handler_Func2);
evntTest.RaiseEvent();
}
static void handler_Func(object sender, EventArgs e)
{
Console.WriteLine("handler_Func");
}
static void handler_Func1(object sender, EventArgs e)
{
Console.WriteLine("handler_Func1");
}
static void handler_Func2(object sender, EventArgs e)
{
Console.WriteLine("handler_Func2");
}
Output will be:
handler_Func
handler_Func1
handler_Func2
handler_Func
handler_Func1
I have an extension method to subscribe a PropertyChanged event of an object that implements INotifyPropertyChanged.
I would like that the event fires just once. Not more.
This is my method.
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
PropertyChangedEventHandler handler = (obj, e) =>
{
if (propertyName == e.PropertyName)
{
action();
}
};
target.PropertyChanged -= handler;
target.PropertyChanged += handler;
}
But it does not work. I cannnot remove the event handler so the event fires every time I call this method.
I have try a different approach. Instead of using annonymous methods, something more traditional, like this:
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
target.PropertyChanged -= target_PropertyChanged;
target.PropertyChanged += target_PropertyChanged;
}
static void target_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//do stuff here
}
And it just works fine. The event fires just once, but I also need the Action parameter. I cannot use it with this approach.
Any workaround or different aproach to solve this issue?Is there something strange with anonymous methods inside static methods?
Thanks in advance.
That is a limitation of using anonymous methods as event handlers. They cannot be removed as you would a normal method (which is technically a delegate instance automatically create via a method group conversion) because anonymous methods get compiled into a compiler-generated container class and a new instance of the class is created each time.
In order to preserve the action parameter you could create a container class which would have the delegate for your event handler inside. The class can be declared private inside the of the other class you're working with - or made internal, maybe in a "Helpers" namespace. It would look something like this:
class DelegateContainer
{
public DelegateContainer(Action theAction, string propName)
{
TheAction = theAction;
PopertyName = propName;
}
public Action TheAction { get; private set; }
public string PropertyName { get; private set; }
public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
if(PropertyName == e.PropertyName)
TheAction();
}
}
Then, create and store the reference to the container in your class. You might create a static member currentContainer and then set the handler like this:
private static DelegateContainer currentContainer;
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
if(currentContainer != null)
target.PropertyChanged -= currentContainer.PropertyChangedHandler;
currentContainer = new DelegateContainer(action, propertyName);
target.PropertyChanged += currentContainer.PropertyChangedHandler;
}
You can get your first example to work if you unsubscribe from within the event handler itself.
public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action)
{
if (target == null)
{
return;
}
// Declare the handler first, in order to create
// a concrete reference that you can use from within
// the delegate
PropertyChangedEventHandler handler = null;
handler = (obj, e) =>
{
if (propertyName == e.PropertyName)
{
obj.PropertyChanged -= handler; //un-register yourself
action();
}
};
target.PropertyChanged += handler;
}
The above code serves as a "one and done" event handler. You can register an unlimited number of these, and each one will only be executed once before unregistering itself.
Keep in mind that it's possible to have one of these handlers execute multiple times, if you raise the event across multiple threads in short succession. To prevent this, you might need to create a static Dictionary(T,T) mapping object instances to "lock objects," and add some sentry code to ensure that a handler is only executed once. Those implementation specifics seem to be a bit outside the scope of your question as currently written, however.
Technically, it's not the same anonymous method you are trying to unsubscribe. .NET creates new instance of that method every time your OnPropertyChanged called. That's why unsubscription will not work.
I have two event handlers wired up to a button click in a Windows form like so:
this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
this.BtnCreate.Click += new System.EventHandler(this.BtnCreate_Click);
both are being called correctly.
However is it possible within FirstHandler() to prevent BtnCreate_Click() being executed? Something like:
void FirstHandler(object sender, EventArgs e)
{
if (ConditionSatisfied)
//Prevent next handler in sequence being executed
}
I know I could just unsubscribe the event, but can this be done programmatically (from within the method)?
As far as I know there is no solution for this. That's because there is no guarantee for the order in which the event handlers are called when the event happens.
Because of that you are not supposed to rely on their order in any way.
Why don't you just replace them with one eventhandler? Something like this:
var rdlc = new RdlcCreator();
this.BtnCreate.Click += (sender, e) => {
rdlc.FirstHandler(sender, e);
if (!rdlc.HasHandledStuff) { // <-- You would need some kind of flag
this.BtnCreate_Click(sender, e);
}
};
That way you can also guarantee the order of the handlers. Alternatively, use the above implementation, but change the signature of FirstHandler to return a bool indicating the condition (as in this case it doesn't really need to have the event's signature anymore):
if (!rdlc.FirstHandler(sender, e)) {
this.BtnCreate_Click(sender, e);
}
EDIT: OR, you just pass the second handler to FirstHandler.
Change the signature of FirstHandler to this:
void FirstHandler(object sender, EventArgs e, EventHandler nextHandler) {
if (ConditionSatisfied) {
// do stuff
}
else if (nextHandler != null) {
nextHandler(sender, e);
}
}
and then:
this.BtnCreate.Click +=
(s, e) => new RdlcCreator().Firsthandler(s, e, this.BtnCreate_Click);
System.ComponentModel namespace contains a CancelEventHandler delegate which is used for this purpose. One of the arguments it provides is a CancelEventArgs instance which contains a boolean Cancel property which can be set be any of the handlers to signal that execution of the invocation list should be stopped.
However, to attach it to a plain EventHandler delegate, you will need to create your own wrapper, something like:
public static class CancellableEventChain
{
public static EventHandler CreateFrom(params CancelEventHandler[] chain)
{
return (sender, dummy) =>
{
var args = new CancelEventArgs(false);
foreach (var handler in chain)
{
handler(sender, args);
if (args.Cancel)
break;
}
};
}
}
For your example, you would use it like this:
this.BtnCreate.Click += CancellableEventChain.CreateFrom(
new RdlcCreator().FirstHandler,
this.BtnCreate_Click
/* ... */
);
Of course, you would need to capture the created chain handler in a field if you need to unsubscribe (detach) it later.
Add the following condition in this.BtnCreate_Click which is the the second event
BtnCreate_Click(object sender, EventArgs e)
{
if (!ConditionSatisfied) //Prevent next handler in sequence being executed
{
// your implementation goes here
}
}
I suggest you to create a some kind of class wrapper. So, you could store there some kind of event flag group (16bit integer, for example) and a few methods to set or unset individual bits (where each means to invoke or not particular EventHandler). You can easily store any count of the Eventhandlers or even Actions, in the class, and invoke in any order you want.
Was finding the solution to the same question, but no luck. So had to resolve myself.
A base class for Cancelable event args
public class CancelableEventArgs
{
public bool Cancelled { get; set; }
public void CancelFutherProcessing()
{
Cancelled = true;
}
}
Next defines the extension method for the EventHandler, note that Invocation List subscribers invoked in backward order (in my case UI elements subscibe the event as they added to components, so which element is rendered later has most visiblility and more priority)
public static class CommonExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SafeInvokeWithCancel<T>(this EventHandler<T> handler, object sender, T args) where T : CancelableEventArgs
{
if (handler != null)
{
foreach (var d in handler.GetInvocationList().Reverse())
{
d.DynamicInvoke(sender, args);
if (args.Cancelled)
{
break;
}
}
}
}
And here is the usage
public class ChessboardEventArgs : CancelableEventArgs
{
public Vector2 Position { get; set; }
}
So if an UI element has some behaviour on the event, it cancells futher processing
game.OnMouseLeftButtonDown += (sender, a) =>
{
var xy = GetChessboardPositionByScreenPosition(a.XY);
if (IsInside(xy))
{
var args = new ChessboardEventArgs { Position = xy };
OnMouseDown.SafeInvokeWithCancel(this, args);
a.CancelFutherProcessing();
}
};
How would you dynamically subscribe to a C# event so that given a Object instance and a String name containing the name of the event, you subscribe to that event and do something (write to the console for example) when that event has been fired?
It would seem using Reflection this isn't possible and I would like to avoid having to use Reflection.Emit if possible, as this currently (to me) seems like the only way of doing it.
/EDIT: I do not know the signature of the delegate needed for the event, this is the core of the problem
/EDIT 2: Although delegate contravariance seems like a good plan, I can not make the assumption necessary to use this solution
You can compile expression trees to use void methods without any arguments as event handlers for events of any type. To accommodate other event handler types, you have to map the event handler's parameters to the events somehow.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
class ExampleEventArgs : EventArgs
{
public int IntArg {get; set;}
}
class EventRaiser
{
public event EventHandler SomethingHappened;
public event EventHandler<ExampleEventArgs> SomethingHappenedWithArg;
public void RaiseEvents()
{
if (SomethingHappened!=null) SomethingHappened(this, EventArgs.Empty);
if (SomethingHappenedWithArg!=null)
{
SomethingHappenedWithArg(this, new ExampleEventArgs{IntArg = 5});
}
}
}
class Handler
{
public void HandleEvent() { Console.WriteLine("Handler.HandleEvent() called.");}
public void HandleEventWithArg(int arg) { Console.WriteLine("Arg: {0}",arg); }
}
static class EventProxy
{
//void delegates with no parameters
static public Delegate Create(EventInfo evt, Action d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, EventArgs x1) => d()
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x"));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"));
var lambda = Expression.Lambda(body,parameters.ToArray());
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
//void delegate with one parameter
static public Delegate Create<T>(EventInfo evt, Action<T> d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg)
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body,parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
//returns an expression that represents an argument to be passed to the delegate
static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if (eventArgs.Type==typeof(ExampleEventArgs) && handlerArgType==typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return Expression.MakeMemberAccess(eventArgs,memberInfo);
}
throw new NotSupportedException(eventArgs+"->"+handlerArgType);
}
}
static class Test
{
public static void Main()
{
var raiser = new EventRaiser();
var handler = new Handler();
//void delegate with no parameters
string eventName = "SomethingHappened";
var eventinfo = raiser.GetType().GetEvent(eventName);
eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,handler.HandleEvent));
//void delegate with one parameter
string eventName2 = "SomethingHappenedWithArg";
var eventInfo2 = raiser.GetType().GetEvent(eventName2);
eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,handler.HandleEventWithArg));
//or even just:
eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,()=>Console.WriteLine("!")));
eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,i=>Console.WriteLine(i+"!")));
raiser.RaiseEvents();
}
}
It's not a completely general solution, but if all your events are of the form
void Foo(object o, T args) , where T derives from EventArgs, then you can use delegate contravariance to get away with it. Like this (where the signature of KeyDown is not the same as that of Click) :
public Form1()
{
Button b = new Button();
TextBox tb = new TextBox();
this.Controls.Add(b);
this.Controls.Add(tb);
WireUp(b, "Click", "Clickbutton");
WireUp(tb, "KeyDown", "Clickbutton");
}
void WireUp(object o, string eventname, string methodname)
{
EventInfo ei = o.GetType().GetEvent(eventname);
MethodInfo mi = this.GetType().GetMethod(methodname, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
Delegate del = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
ei.AddEventHandler(o, del);
}
void Clickbutton(object sender, System.EventArgs e)
{
MessageBox.Show("hello!");
}
It is possible to subscribe to an event using Reflection
var o = new SomeObjectWithEvent;
o.GetType().GetEvent("SomeEvent").AddEventHandler(...);
http://msdn.microsoft.com/en-us/library/system.reflection.eventinfo.addeventhandler.aspx
Now here is going to be the problem that you are going to have to solve. The delegates required for each event handler will have different signatures. You are going to have to find away to create these methods dynamically, which probably means Reflection.Emit, or you are going to have to limit your self to a certain delegate so that you can handle it with compiled code.
Hope this helps.
public TestForm()
{
Button b = new Button();
this.Controls.Add(b);
MethodInfo method = typeof(TestForm).GetMethod("Clickbutton",
BindingFlags.NonPublic | BindingFlags.Instance);
Type type = typeof(EventHandler);
Delegate handler = Delegate.CreateDelegate(type, this, method);
EventInfo eventInfo = cbo.GetType().GetEvent("Click");
eventInfo.AddEventHandler(b, handler);
}
void Clickbutton(object sender, System.EventArgs e)
{
// Code here
}
Try LinFu--it has a universal event handler that lets you bind to any event at runtime. For example, here's you you can bind a handler to the Click event of a dynamic button:
// Note: The CustomDelegate signature is defined as:
// public delegate object CustomDelegate(params object[] args);
CustomDelegate handler = delegate
{
Console.WriteLine("Button Clicked!");
return null;
};
Button myButton = new Button();
// Connect the handler to the event
EventBinder.BindToEvent("Click", myButton, handler);
LinFu lets you bind your handlers to any event, regardless of the delegate signature. Enjoy!
You can find it here:
http://www.codeproject.com/KB/cs/LinFuPart3.aspx
I recently wrote a series of blog posts describing unit testing events, and one of the techniques I discuss describes dynamic event subscription. I used reflection and MSIL (code emitting) for the dynamic aspects, but this is all wrapped up nicely. Using the DynamicEvent class, events can be subscribed to dynamically like so:
EventPublisher publisher = new EventPublisher();
foreach (EventInfo eventInfo in publisher.GetType().GetEvents())
{
DynamicEvent.Subscribe(eventInfo, publisher, (sender, e, eventName) =>
{
Console.WriteLine("Event raised: " + eventName);
});
}
One of the features of the pattern I implemented was that it injects the event name into the call to the event handler so you know which event has been raised. Very useful for unit testing.
The blog article is quite lengthy as it is describing an event unit testing technique, but full source code and tests are provided, and a detailed description of how dynamic event subscription was implemented is detailed in the last post.
http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/
What you want can be achieved using dependency injection. For example Microsoft Composite UI app block does exactly what you described
This method adds to an event, a dynamic handler that calls a method OnRaised, passing the event parameters as an object array:
void Subscribe(object source, EventInfo ev)
{
var eventParams = ev.EventHandlerType.GetMethod("Invoke").GetParameters().Select(p => Expression.Parameter(p.ParameterType)).ToArray();
var eventHandler = Expression.Lambda(ev.EventHandlerType,
Expression.Call(
instance: Expression.Constant(this),
method: typeof(EventSubscriber).GetMethod(nameof(OnRaised), BindingFlags.NonPublic | BindingFlags.Instance),
arg0: Expression.Constant(ev.Name),
arg1: Expression.NewArrayInit(typeof(object), eventParams.Select(p => Expression.Convert(p, typeof(object))))),
eventParams);
ev.AddEventHandler(source, eventHandler.Compile());
}
OnRaised has this signature:
void OnRaised(string name, object[] parameters);
Do you mean something like:
//reflect out the method to fire as a delegate
EventHandler eventDelegate =
( EventHandler ) Delegate.CreateDelegate(
typeof( EventHandler ), //type of event delegate
objectWithEventSubscriber, //instance of the object with the matching method
eventSubscriberMethodName, //the name of the method
true );
This doesn't do the subscription, but will give to the method to call.
Edit:
Post was clarified after this answer, my example won't help if you don't know the type.
However all events in .Net should follow the default event pattern, so as long as you've followed it this will work with the basic EventHandler.