Reputation: 6471
I am tracking if an event has been fired like this:
bool IsFormLoaded;
private void Form1_Load(object sender, EventArgs e)
{
//Do stuff
IsFormLoaded = true;
}
private void button1_Click(object sender, EventArgs e)
{
//Do stuff
}
But doing this for many events is not elegant so I want a solution that lets me check if any event was fired like this:
bool IsFormLoaded = IsEventFired(Form1_Loaded);
bool IsButton1Clicked = IsEventFired(Button1_Click);
Upvotes: 3
Views: 6134
Reputation: 31116
Funny question, seems to me like something you don't want to write over and over again. That's why I would rather go for a single generic component, than a hashset or something like that. Also, since form implementations are normally based on threads, I use a concurrent dictionary.
This solution can be improved in a couple of different ways; most obviously making the handling more generic as well and lack of the 0-parameter handler. I kept it as simple as possible for clearance. Perhaps I'll probably post something more complete on my blog in a couple of days; if I do I'll share the info here.
My solution has 2 parts: (1) a generic hook class and (2) the implementation in the form. Currently the solution is lazy, e.g. I put event handlers at the end, not at the front of the queue. You should be able to fix this by using GetInvocationList or something similar.
The generic hook class basically hooks events and keeps track if an event is called:
public class EventHooks
{
private class EventHooksEquality : IEqualityComparer<Tuple<string, object>>
{
public bool Equals(Tuple<string, object> x, Tuple<string, object> y)
{
return x.Item1.Equals(y.Item1) && object.ReferenceEquals(x.Item2, y.Item2);
}
public int GetHashCode(Tuple<string, object> obj)
{
return obj.Item1.GetHashCode();
}
}
private ConcurrentDictionary<Tuple<string, object>, bool> called =
new ConcurrentDictionary<Tuple<string, object>, bool>(new EventHooksEquality());
private abstract class BaseHookHandler
{
protected BaseHookHandler(object container, string eventName, EventHooks hooks)
{
this.hooks = hooks;
this.container = container;
this.eventName = eventName;
}
protected string eventName;
protected object container;
protected EventHooks hooks;
}
private class HookHandler<T1> : BaseHookHandler
{
public HookHandler(object container, string eventName, EventHooks hooks)
: base(container, eventName, hooks)
{
}
public void Handle(T1 t1)
{
hooks.called.TryAdd(new Tuple<string, object>(eventName, container), true);
}
}
private class HookHandler<T1, T2> : BaseHookHandler
{
public HookHandler(object container, string eventName, EventHooks hooks)
: base(container, eventName, hooks)
{
}
public void Handle(T1 t1, T2 t2)
{
hooks.called.TryAdd(new Tuple<string, object>(eventName, container), true);
}
}
// add more handlers here...
public void HookAll(object obj)
{
foreach (var eventHandler in obj.GetType().GetEvents())
{
Hook(obj, eventHandler.Name);
}
}
public void Hook(object obj, string eventHandler)
{
if (obj == null)
{
throw new Exception("You have to initialize the object before hooking events.");
}
// Create a handler with the right signature
var field = obj.GetType().GetEvent(eventHandler);
var delegateInvoke = field.EventHandlerType.GetMethod("Invoke");
Type[] parameterTypes = delegateInvoke.GetParameters().Select((a) => (a.ParameterType)).ToArray();
// Select the handler with the correct number of parameters
var genericHandler = Type.GetType(GetType().FullName + "+HookHandler`" + parameterTypes.Length);
var handlerType = genericHandler.MakeGenericType(parameterTypes);
var handlerObject = Activator.CreateInstance(handlerType, obj, eventHandler, this);
var handler = handlerType.GetMethod("Handle");
// Create a delegate
var del = Delegate.CreateDelegate(field.EventHandlerType, handlerObject, handler);
// Add the handler to the event itself
field.AddEventHandler(obj, del);
}
public bool IsCalled(object obj, string eventHandler)
{
return called.ContainsKey(new Tuple<string, object>(eventHandler, obj));
}
}
Usage in a class can be done as follows (example):
public Form1()
{
InitializeComponent();
hooks.HookAll(this);
// or something like: hooks.Hook(this, "Load");
hooks.Hook(button1, "Click");
}
private EventHooks hooks = new EventHooks();
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
this.textBox1.Text =
string.Format("Load: {0}\r\nClick: {1}\r\nButton click: {2}\r\n",
hooks.IsCalled(this, "Load"),
hooks.IsCalled(this, "Click"),
hooks.IsCalled(button1, "Click"));
}
Upvotes: 1
Reputation: 45096
Similar to answer from Dhawalk. I did not see that answer before I wrote this.
private HashSet<string> events = new HashSet<string>();
private void IsLoaded(object sender, RoutedEventArgs e)
{
// check
System.Diagnostics.Debug.WriteLine(CheckEvents("IsLoaded", true).ToString());
// add
System.Diagnostics.Debug.WriteLine(CheckEvents("IsLoaded", false).ToString());
// check
System.Diagnostics.Debug.WriteLine(CheckEvents("IsLoaded", true).ToString());
}
private bool CheckEvents(string Event, bool CheckAdd)
{
// CheckAdd True to check
// CheckAdd Fasle to add
bool result = events.Contains(Event);
if (!result && !CheckAdd) events.Add(Event);
return result;
}
Upvotes: 0
Reputation: 13033
You are handling events using designer. You can do it for example in constructor like follows:
this.Load += delegate { IsFormLoaded = true; };
button1.Click += delegate { IsButton1Clicked = true; };
IMO it's more elegant :)
Upvotes: 5
Reputation: 1269
Write your own Base Form(deriving from the Windows.Forms.Form), and override the event firing methods to capture if an event was fired or not. By having a base class, you will be able to reuse your event monitoring logic in all your forms.
Here's some example code that you can use. I have used only Loaded event here. You will have to do this for all the events you want to monitor. You can also use enum than using the constants. Hope this helps
const string OnLoadFired = "OnLoadFired";
const string OnShownFired = "OnShownFired";
List<string> eventsFired = new List<string>();
protected override void OnLoad(EventArgs e)
{
if(!eventsFired.Contains(OnLoadFired))
{
eventsFired.Add(OnLoadFired);
}
base.OnLoad(e);
}
public bool IsEventFired(string eventName)
{
return eventsFired.Contains(eventName);
}
Upvotes: 0