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.
Related
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.
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}
I am attempting to subscribe two events to an object. But the object is not instantiated before I try to add the events. Is there a way I can subscribe these two events and instantiate afterwards? I already have the delegates, event, event args and event handler working.
Sample Code:
Ares a;
public B()
{
a.up += new upEventHandler(doUp);
a.down += new downEventHandler(doDown);
a = new Ares();
}
I am attempting to subscribe two events to an object. But the object is not instantiated before I try to add the events. Is there a way I can subscribe these two events and instantiate afterwards?
No, absolutely not. It's exactly like trying to set properties on an object before the object exists. Try to think about how that would work - and then realize that subscribed event handlers are part of the state of an object just like properties are.
Obviously you could store the event handlers somewhere else and subscribe them later on, but as stated, the answer is simply no. It doesn't make any sense at a conceptual level, or a practical one.
It's not possible. You must instantiate the object first.
The closest thing you could do to what you're describing would be to make the events static.
class Ares {
public static event upEventHandler up;
public static event downEventHandler down;
// ...
}
And then modify B() to be:
public B() {
Ares.up += new upEventHandler(doUp);
Ares.down += new downEventHandler(doDown);
a = new Ares();
}
I assume that the events are fired in the constructor and you want to capture that.
Try refactoring the event firing code out of the constructor into a separate Initialize() method, so you would then have the following:
Ares a;
public B()
{
a = new Ares();
a.up += new upEventHandler(doUp);
a.down += new downEventHandler(doDown);
a.Initialize(); //do all init of the ares object here, not in constructor
}
I am building a simple class to hold related methods. Part of this code includes synchronising to a database. The built in SyncOrchestrator class includes a SessionProgress event handler which I can wire up an event to.
What I would like to do is instance my class and then hook up an some code to this event so that I can display a progress bar (ill be using BGWorker).
So, my question is probably c# 101, but how do I expose this event through my class the correct way so that I can wire it up?
Thanks
I think you're looking for something like this:
(I also suggest you read the Events tutorial on MSDN.)
public class SyncOrchestrator
{
// ...
public event EventHandler<MyEventArgs> SessionProgress;
protected virtual void OnSessionProgress(MyEventArgs e)
{
// Note the use of a temporary variable here to make the event raisin
// thread-safe; may or may not be necessary in your case.
var evt = this.SessionProgress;
if (evt != null)
evt (this, e);
}
// ...
}
where the MyEventArgs type is derived from the EventArgs base type and contains your progress information.
You raise the event from within the class by calling OnSessionProgress(...).
Register your event handler in any consumer class by doing:
// myMethodDelegate can just be the name of a method of appropiate signature,
// since C# 2.0 does auto-conversion to the delegate.
foo.SessionProgress += myMethodDelegate;
Similarly, use -= to unregister the event; often not explicitly required.
Like this:
public event EventHandlerDelegate EventName;
EventHandlerDelegate should obviously be the name of a delegate type that you expect people to provide to the event handler like so:
anObject.EventName += new EventHandlerDelegate(SomeMethod);
When calling the event, make sure you use this pattern:
var h = EventName;
if (h != null)
h(...);
Otherwise you risk the event handler becoming null in between your test and actually calling the event.
Also, see the official documentation on MSDN.
How can the event System.ComponentModel.CancelEventArgs be used? Suppose we have the following code:
public event CancelEventHandler EventTest = delegate { };
public void MakeSomethingThatRaisesEvent()
{
CancelEventArgs cea = new CancelEventArgs();
EventTest(this, cea);
if (cea.Cancel)
{
// Do something
}
else
{
// Do something else
}
}
What happens if more than one delegate is registered on the event? There is any way to get the results of all the subscribers?
This is used on Winforms (at least) sometimes. If not possible to get all values, they suppose only one subscriber to the event?
To ask each subscriber separately, you need to access the list:
foreach (CancelEventHandler subHandler in handler.GetInvocationList())
{
// treat individually
}
Then you can check each in turn; otherwise you just get the final vote.
Normally, in most cases, the class just allows multiple subscribers, but each gets the same instance of CancelEventArgs.
If any of the subscribers set Cancel to true, the operation will be treated as canceled.
You can work around this by getting the invocation list, and sending an event to each subscriber, but this is not usually necessary.