My code is as follows.
Control[] FoundControls = null;
FoundControls = MyFunctionToFilter(TF, c => c.Name != null && c.Name.StartsWith("grid"));
var eventinfo = FoundControls[0].GetType().GetEvents();
However, eventinfo gives me the list of all the controls which belongs to the grid.
Whereas there are only two events that are defined that is KeyDown and Validating in the main class.
How can I get a list of these assigned events, that is, Keydown and Validating?
Windows Forms (WinForms) has a tricky model of events for components (and DataGridView is a component). Some events are inherited from Control (like FontChanged, ForeColorChanged, etc.), but all specific to component events are stored in a single EventHandlerList object, which is inherited from Component (BTW, events from Control are also stored there, see the update at the end of the answer). There is a protected Events property for that:
protected EventHandlerList Events
{
get
{
if (this.events == null)
this.events = new EventHandlerList(this);
return this.events;
}
}
And here is the way how event handlers are added for DataGridView events:
public event DataGridViewCellEventHandler CellValueChanged
{
add { Events.AddHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); }
remove { Events.RemoveHandler(EVENT_DATAGRIDVIEWCELLVALUECHANGED, value); }
}
As you can see, delegate (value) is passed to EventHandlerList with some key value. All event handlers are stored there by key. You can think about EventHandlerList as a dictionary with objects as keys, and delegates as values. So, here is how you can get components' events with reflection. The first step is getting those keys. As you already noticed, they are named as EVENT_XXX:
private static readonly object EVENT_DATAGRIDVIEWCELLVALUECHANGED;
private static readonly object EVENT_DATAGRIDVIEWCELLMOUSEUP;
// etc.
So here we go:
var keys = typeof(DataGridView) // You can use `GetType()` of component object.
.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(f => f.Name.StartsWith("EVENT_"));
Next, we need our EventHandlerList:
var events = typeof(DataGridView) // or GetType()
.GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
// Could be null, check that
EventHandlerList handlers = events.GetValue(grid) as EventHandlerList;
And the last step, getting the list of keys, which have handlers attached:
var result = keys.Where(f => handlers[f.GetValue(null)] != null)
.ToList();
That will give you the keys. If you need delegates, then simply look in the handlers list for them.
UPDATE: Events which inherited from Control are also stored in EventHandlerList, but for some unknown reason their keys have different names, like EventForeColor. You can use the same approach as above to get those keys and check if handlers are attached.
According to the comments in this question showing that this is a Windows Forms related question it's almost not possible to determine assigned event handlers by iterating the list, not without reflection anyway.
The following text is from Hans Passant's answer on a similar question:
Windows Forms has strong counter-measures against doing this. Most
controls store the event handler reference in a list that requires a
secret 'cookie'. The cookie value is dynamically created, you cannot
guess it up front. Reflection is a backdoor, you have to know the
cookie variable name. The one for the Control.Click event is named
"EventClick" for example, you can see this in the Reference Source or
with Reflector.
Knowing the cookie name would help you get the assigned event, but I am not sure if there is such a list with all the names which you could iterate. See this other answer which demonstrates how to get an event from one control where the cookie name is known.
Here you can find another example (which still goes by using a known event name).
Can't you use reflection to look the list of handlers?
Here is a simple console app looking at handlers hooked on the events of a serial port instance:
using System;
using System.IO.Ports;
using System.Reflection;
class Program
{
static void OnErrorReceived(object sender, SerialErrorReceivedEventArgs e){}
static void Main(string[] args)
{
var serialPort = new SerialPort();
// Add a handler so we actually get something out.
serialPort.ErrorReceived += OnErrorReceived;
foreach (var eventInfo in serialPort.GetType().GetEvents())
{
var field = serialPort.GetType().GetField(eventInfo.Name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField);
if (field != null)
{
var backingDelegate = (Delegate)field.GetValue(serialPort);
if (backingDelegate != null)
{
var subscribedDelegates = backingDelegate.GetInvocationList();
foreach (var subscribedDelegate in subscribedDelegates)
{
Console.WriteLine(subscribedDelegate.Method.Name + " is hooked on " + eventInfo.Name);
}
}
}
}
}
}
Based on Kyle comment:
#Jacob Yes.... Because these are the only two events declared in the
main class – Kyle
Events would contain KeyDown and Validating events only.
Control a = new TextBox();
var events = a.GetType().GetEvents().Where(eventInfo => eventInfo != null && (eventInfo.Name == "KeyDown" || eventInfo.Name == "Validating"));
+events.ToList()[0] {System.Windows.Forms.KeyEventHandler KeyDown}
+events.ToList()[1] {System.ComponentModel.CancelEventHandler Validating}
Related
In my .NET application I am subscribing to events from another class. The subscription is conditional. I am subscribing to events when the control is visible and de-subscribing it when it become invisible. However, in some conditions I do not want to de-subscribe the event even if the control is not visible as I want the result of an operation which is happening on a background thread.
Is there a way through which I can determine if a class has already subscribed to that event?
I know we can do it in the class which will raise that event by checking the event for null, but how do I do it in a class which will subscribe to that event?
The event keyword was explicitly invented to prevent you from doing what you want to do. It restricts access to the underlying delegate object so nobody can directly mess with the events handler subscriptions that it stores. Events are accessors for a delegate, just like a property is an accessor for a field. A property only permits get and set, an event only permits add and remove.
This keeps your code safe, other code can only remove an event handler if it knows the event handler method and the target object. The C# language puts an extra layer of security in place by not allowing you to name the target object.
And WinForms puts an extra layer of security in place so it becomes difficult even if you use Reflection. It stores delegate instances in an EventHandlerList with a secret "cookie" as the key, you'd have to know the cookie to dig the object out of the list.
Well, don't go there. It is trivial to solve your problem with a bit of code on your end:
private bool mSubscribed;
private void Subscribe(bool enabled)
{
if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged;
else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged;
mSubscribed = enabled;
}
Assuming that you have no access to the innards of the class declaring the event, you have no way to do it directly. Events only expose operators += and -=, nothing else. You will need a flag or some other mechanism in your subscribing class to know whether you are already subscribed or not.
/// <summary>
/// Determine if a control has the event visible subscribed to
/// </summary>
/// <param name="controlObject">The control to look for the VisibleChanged event</param>
/// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns>
private bool IsSubscribed(Control controlObject)
{
FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible",
BindingFlags.Static | BindingFlags.NonPublic);
object object_value = event_visible_field_info.GetValue(controlObject);
PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null);
return (event_list[object_value] != null);
}
Simply check whether the control is visible or not whenever the event handler is triggered.
Can you put the decision making logic into the method that fires the event? Assuming you're using Winforms it'd look something like this:
if (MyEvent != null && isCriteriaFulfilled)
{
MyEvent();
}
Where isCriteriaFulfilled is determined by your visible/invisible logic.
// UPDATES /////
Further to your 1st comment would it not make sense to alter the behaviour inside your event handler depending on the value of this.Visible?
a.Delegate += new Delegate(method1);
...
private void method1()
{
if (this.Visible)
// Do Stuff
}
Or if you really have to go with subscribing and unsubscribing:
private Delegate _method1 = null;
...
if(this.visible)
{
if (_method1 == null)
_method1 = new Delegate(method1);
a.Delegate += _method1;
}
else if (_method1 != null)
{
a.Delegate -= _method1;
}
using System;
//...
public event EventHandler Event;
public bool IsSubscribed(EventHandler Delegate)
{
if (Event == null)
{
return false;
}
var InvocationList = Event.GetInvocationList();
foreach (var Entry in InvocationList)
{
if (Entry.Equals(Delegate))
{
return true;
}
}
return false;
}
After 12 years it is here, works for me.
Can't you just remember whether you already subscribed? That approach worked fine for me so far. Even if you have a lot of events or objects, you may still want to just remember that (in a dictionary, for example).
On the other hand, visibility change was, at least for me, not a good point to subscribe/unsubscribe. I typically rather go with construction / Disposed, which are more clear than each time visibility changes.
I'm just expanding on Hans' answer. I'm just trying to ensure that I'm not installing my handler more than once, and not removing it when I still need it. This doesn't protect from a malicious or malfeasant caller unsubscribing repeatedly, for that you'd need to track the callers, and that would just open you up to having repeated subscriptions overrun the tracking mechanism.
// Tracks how many times the ReflectionOnlyResolveHandler has been requested.
private static int _subscribers = 0;
/// <summary>
/// Register or unregister the ReflectionOnlyResolveHandler.
/// </summary>
/// <param name="enable"></param>
public static void SubscribeReflectionOnlyResolve(bool enable)
{
lock(_lock)
{
if (_subscribers > 0 && !enable) _subscribers -= 1;
else if (enable) _subscribers += 1;
if (enable && _subscribers == 1)
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler;
else if (_subscribers == 0)
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler;
}
}
Context:
I have an unknown Model-Object which contains one or more ObservableCollections.
These collections contains objects which implements INotifyPropertyChanged.
I now want to observe the PropertyChangedEvents for all objects inside the Model-Object.
What I have done:
So I wrote this method which uses reflection to find the specific objects. This all works fine. Except the part where it comes to the MvvmCross function WeakSubscribe. I really like the idea behind, but it seems it loses the reference and don't fire the event.
Strange: If I debug this code it works correct, but without breakpoints it's not working.
private void SubscribeToDetailData()
{
var tempTokenList = new List<MvxNotifyPropertyChangedEventSubscription>();
var fieldInfos =
DetailData.GetType().GetRuntimeProperties().Where(f => Helpers.IsSubclassOfRawGeneric(typeof (ObservableCollection<>), f.PropertyType));
foreach (var fieldInfo in fieldInfos)
{
var collection = fieldInfo.GetValue(DetailData) as IEnumerable<object>;
if (collection == null)
continue;
foreach (var inpc in collection.Cast<INotifyPropertyChanged>())
{
tempTokenList.Add(inpc.WeakSubscribe((sender, e) => DetailDataPropertyChanged(e.PropertyName)));
}
}
_subscriptionTokens = tempTokenList.ToArray();
}
// This method is never raised
private void DetailDataPropertyChanged(string propertyName)
{
if (_enabledFields.Evaluate(DetailData, propertyName))
RaisePropertyChanged(() => FieldEnabledState);
}
It might be that your subscribed Action is getting garbage collected.
This can be caused, I think, if the compiler creates an instance of an anonymous class to implement your anonymous Action. I wouldn't normally expect this to happen in your code because you are not using any local variables in your Action - but this could be the case.
Does your code work if you change the subscription to:
tempTokenList.Add(inpc.WeakSubscribe(DetailDataPropertyChanged));
with the method signature changed to:
private void DetailDataPropertyChanged(object sender, PropertyChangedEventArgs e)
How can i check whether ComboBox.SelectIndexchanged Event does not holding any method.
Here i am having methods to Add and Rovemo methods to and from ComboBox which can serve for any comboBox.
public static void AddMethodToComoBox(EventHandler MetodName, ComboBox cbm)
{
if(cbm.SelectedIndexChanged==null)
{
cm.SelectedIndexChanged += MetodName;
}
}
public static void RemoveMethodToComoBox(EventHandler MetodName, ComboBox cbm)
{
if (cbm.SelectedIndexChanged != null)
{
cbm.SelectedIndexChanged -= MetodName;
}
}
If i want to add a method means simply i will call this add method and pass CmoboBox object and Method need to Add similarly to Romove.
But the problem here is if i click a comboBox twice then the method will call twice.
so to avoid that i am checking whether the ComboBox's selectedIndexChanged event is already holding any Mthod. If it is then code will not add the same method again. For that i used the If Condition. but it showing error.
How can i achieve this???
Your problem is you need to access to the EventHandlerList of the ComboBox, this event handler list is not exposed publicly, so we have to use a little reflection. The key to get the handler of the SelectedIndexChanged event is saved as a field called EVENT_SELECTEDINDEXCHANGED in the ComboBox class, this field is also non-public, so we also have to use reflection to get it, once got both EventHanlderList and the SelectedIndexChanged event key, we can check if that key passing in the indexer of EventHandlerList returns null or not, returning null means there is not any handler for the event SelectedIndexChanged:
//Get the field EVENT_SELECTEDINDEXCHANGED
var eventSelectedIndexChangedKey = typeof(ComboBox).GetField("EVENT_SELECTEDINDEXCHANGED",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Static)
.GetValue(comboBox1);
//Get the event handler list of the comboBox1
var eventList = typeof(ComboBox).GetProperty("Events",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance)
.GetValue(comboBox1, null) as EventHandlerList;
//check if there is not any handler for SelectedIndexChanged
if(eventList[eventSelectedIndexChangedKey] == null){
//....
} else {
//....
}
However, I feel that your problem is just to avoid adding duplicated (or twice) a handler for the event SelectedIndexChanged, so you can always try unregistering the handler first before assigning, it won't never throw exception:
public static void AddMethodToComoBox(EventHandler MetodName, ComboBox cbm)
{
cm.SelectedIndexChanged -= MethodName;
cm.SelectedIndexChanged += MethodName;
}
public static void RemoveMethodToComoBox(EventHandler MetodName, ComboBox cbm)
{
cbm.SelectedIndexChanged -= MetodName;//won't never throw exception
}
Well you can check if any handler is attached to the event in the class where it the event is declared. If you try to do a check here you will get something like this:
The event SelectedIndexChanged can only appear on the left hand side of += or -=
Your best option is to have a dictionary to store the control and the event added for it.
I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.
I have a class that manages keyboard input and fires off KeyPressed, KeyReleased, or KeyHeld events. It only fires off an event if the key exists in the KeyBindings collection of my Controller component. Now that I've got all of that working I'm stuck on a problem. What I want is the following:
Key pressed.
if(Key bind exists)
Fire key pressed event.
foreach(function in keyBinds)
{
execute function, fire event, whatever...
}
I just can't figure out how the foreach loop would work. Any ideas on how I could pull something like this off?
KeyboardController Component:
public class KeyboardController : IComponent
{
//Fields
private Dictionary<Keys, HashSet<Delegate>> m_keyBindings = new Dictionary<Keys,HashSet<Delegate>>();
//Properties
public Dictionary<Keys, HashSet<Delegate>> KeyBindings
{
get { return m_keyBindings; }
}
}
This is the class that will contain the Keys and their function/delegate/event/whatever bindings. The code for events CANNOT be contained within this class because the class is meant only to store data. I need to pass a Key bind and an action or set of actions to perform when this bind is pressed.
Adding a bind:
//Set key bindings
KeyboardController kbController = entityManager.GetComponent<KeyboardController>(1);
kbController.KeyBindings.Add(Keys.Up, new HashSet<Delegate>());
kbController.KeyBindings[Keys.Up].Add(function);
I don't know how to make the third line in "Adding a bind:" work.
You can use a multicast delegate to automatically fire off multiple events for a given key, this way you don't need to maintain a collection of events. For example:
Dictionary<Key, Action> bindings = ...
Action binding;
if (binding.TryGetValue(key, out binding))
binding(); // this will execute multiple events if they are hooked
Hooking events:
bindings[Keys.A] += new Action(MyAKeyHandler);
If for some reason you didn't want to use multicast delegates, you could do something like this:
List<Action> handlers = binding[key];
...
if (handlers != null)
foreach (var handler in handlers)
handler();
Rather than HashSet use an actual delegate type such as Action<>. For instance:
Dictionary<Keys, Action> handlers = ...
handlers[key] += function;
Since C# has delegate types as first-class language objects, you can just keep a collection of functions quite directly
var keyBinds = new List<Action<KeyPressEventArgs>>();
KeyPressEventArgs args = /* Something from the actual keypress event */;
foreach (Action<KeyPressEventArgs> function in keyBinds)
{
function(args);
}
You may use collection of Delegates for this purpose. Check this link: http://xenta.codeplex.com/SourceControl/changeset/view/068ddfd6bf36#trunk%2fSrc%2fFwk%2fXenta.EventBroker.Default%2fBroker.cs. It's an event broker, where we use lists of delegates.