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.
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 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();
}
};
I'm doing a small multi-threaded app that uses asynchronous TCP sockets, but I will get to the point: I'm using a custom event to read a value from a form and the delegate used by the event returns a string when finished.
My question here is: is that correct? is it OK to return values from the events? or is there a better way to do this? (like using a simple delegate to the form to read the values)
It's often awkward to return values from events. In practice, I've found it much easier to include a writable property on a set of custom EventArgs that is passed to the event, and then checked after the event fires -- similar to Cancel property of the WinForms FormClosing event.
I don't think it's a good idea... events are basically multicast delegates, so there can be multiple handlers. Which return value will you take in that case ?
I know this is ages after the post but thought of adding comment with code to explain Dustin Campbell answer for if someone else comes across this thread. I came across this post while trying to decide what would be best practice and this is what is meant by the answer.
Create your own custom event handler class
public class myCustomeEventArgs:EventArgs
{
public bool DoOverride { get; set; }
public string Variable1 { get; private set; }
public string Variable2{ get; private set; }
public myCustomeEventArgs(string variable1 , string variable2 )
{
DoOverride = false;
Variable1 = variable1 ;
Variables = variable2 ;
}
}
So when you create your event delegate you use your created event args like this.
public delegate void myCustomeEventHandler(object sender, myCustomeEventArgs e);
And in the class raising the event you declare the event.
public event myCustomeEventHandler myCustomeEvent;
So when you trigger the event in your class the class that listens for the event you can just in the body of the event set e.DoOverride = true; as it will be declared in the class firing the event.
Fire event for example:
if(myCustomeEvent != null)
{
var eventArgs = new myCustomeEventArgs("Some Variable", "Another Varaible");
myCustomeEvent(this, eventArgs);
//Here you can now with the return of the event work with the event args
if(eventArgs.DoOverride)
{
//Do Something
}
}
The closest example I can think of is the FormClosing event in WinForms. It lets the form cancel the event by setting the eventArgs.Cancel property to true. For you to do something similar, you would define your own event args class with the return value as a property on that class. Then pass an event args object whenever you raise the event. Whoever raised the event can inspect the event args object for the return value. Others who are receiving the event can also inspect or change the event args object.
Update: I just ran across the AppDomain.AssemblyResolve event, and it appears to be an event that returns a value. It seems you just need to declare a delegate type that returns a value, and then define your event with that delegate type. I haven't tried creating my own event like this, though. One advantage to using a property on the event argument is that all subscribers to the event can see what previous subscribers have returned.
Note: only the last event returns the result.
class Program
{
static event Func<string, bool> TheEvent;
static void Main(string[] args)
{
TheEvent += new Func<string, bool>(Program_TheEvent);
TheEvent +=new Func<string,bool>(Program_TheEvent2);
TheEvent += new Func<string, bool>(Program_TheEvent3);
var r = TheEvent("s"); //r == flase (Program_TheEvent3)
}
static bool Program_TheEvent(string arg)
{
return true;
}
static bool Program_TheEvent2(string arg)
{
return true;
}
static bool Program_TheEvent3(string arg)
{
return false;
}
}
I don't know if this is best practice but i did it this way.
Func<DataRow, bool> IsDataValid;
// some other code ....
isValid = true;
if (IsDataValid != null)
{
foreach (Func<DataRow, bool> func in IsDataValid.GetInvocationList())
{
isValid &= func(Row);
}
}
If event returns a value and there are multiple handlers registered the event returns the result value of the last called handler.
Look for an example at http://blogs.msdn.com/b/deviations/archive/2008/11/27/event-handlers-returning-values.aspx
I looped over the properties of the EventArgs like this and pulled out its X and Y values.
private void navBarControl1_Click(object sender, EventArgs e)
{
int _x = 0;
int _y = 0;
Type t = e.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(t.GetProperties());
foreach (PropertyInfo prop in props)
{
if (prop.Name == "X")
{
object propValue = prop.GetValue(e, null);
_x = Convert.ToInt32(propValue);
}
if (prop.Name == "Y")
{
object propValue = prop.GetValue(e, null);
_y = Convert.ToInt32(propValue);
}
}
void method()
{
list<string> strings = new list<string>();
dostuff += stuff;
dostuff += stuff;
dostuff(this, new EventHandlerArgs(){ Parameter = strings })
foreach(string currString in strings)
{
//....
}
}
void stuff(object sender, EventHandlerArgs e)
{
list<string> strings = e.Parameter as list<string>;
if (strings != null)
{
strings.Add(MyString)
}
}
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