检查事件是否至少被触发过一次
本文关键字:一次 是否 事件 检查 | 更新日期: 2023-09-27 17:50:57
我正在跟踪一个事件是否像这样被触发:
bool IsFormLoaded;
private void Form1_Load(object sender, EventArgs e)
{
//Do stuff
IsFormLoaded = true;
}
private void button1_Click(object sender, EventArgs e)
{
//Do stuff
}
但是对于许多事件这样做并不优雅,所以我想要一个解决方案,让我检查是否有像这样的事件被触发:
bool IsFormLoaded = IsEventFired(Form1_Loaded);
bool IsButton1Clicked = IsEventFired(Button1_Click);
您正在使用设计器处理事件。例如,您可以在构造函数中这样做:
this.Load += delegate { IsFormLoaded = true; };
button1.Click += delegate { IsButton1Clicked = true; };
在我看来它更优雅:)
有趣的问题,在我看来好像你不想一遍又一遍地写。这就是为什么我宁愿使用单一的通用组件,而不是散列集或类似的东西。另外,由于表单实现通常是基于线程的,所以我使用了并发字典。
这个解决方案可以通过几种不同的方式进行改进;最明显的是使处理更通用,并且缺少0参数处理程序。为了通关,我尽量简化了。也许过几天我可能会在我的博客上发布一些更完整的东西;如果我有,我会在这里分享信息。
我的解决方案有两部分:(1)一个泛型钩子类和(2)窗体中的实现。目前的解决方案是懒惰的,例如,我把事件处理程序放在队列的末尾,而不是前面。你应该能够通过使用GetInvocationList或类似的东西来修复这个问题。
泛型钩子类基本上钩子事件并跟踪事件是否被调用:
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));
}
}
在类中的使用方法如下(示例):
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"));
}
编写自己的Base Form(派生自Windows.Forms.Form),并覆盖事件触发方法来捕获事件是否被触发。通过拥有一个基类,您将能够在所有表单中重用您的事件监视逻辑。
这里有一些您可以使用的示例代码。我在这里只使用了Loaded事件。对于想要监视的所有事件,都必须这样做。除了使用常量,还可以使用enum。希望能有所帮助
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);
}
与Dhawalk的回答类似。在我写这篇文章之前,我没有看到那个答案。
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;
}