我正在为Windows Forms编写一些单元测试,到目前为止已经能够弄清楚如何使用Reflection设置私有控件的属性并调用它们的方法。但是我被困在如何关联一个内联lambda附加自己到一个控件上发生的事件,在这种情况下,DataGridView的DataSourceChanged事件。
public static void ObserveGrid(this Form form, string controlName, Action action)
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0] as DataGridView;
if (control != null)
{
EventInfo ei = typeof(DataGridView).GetEvent("DataSourceChanged");
if (ei != null)
{
ei.AddEventHandler(control, Delegate.CreateDelegate(ei.EventHandlerType, control, action.Method ));
}
}
}
}
我希望这样称呼它:
var monitor = new Mutex();
form.ObserveGrid("dataGridView1",
() =>
{
Trace.WriteLine("Releasing mutex.");
monitor.ReleaseMutex();
});
var sw = new Stopwatch();
form.ClickButton("btnSearch", sw);
monitor.WaitOne();
sw.Stop();
在执行过程中,我得到一个错误:
无法绑定到目标方法,因为它的签名或安全性透明度与委托类型不兼容。
在这种情况下我做错了什么?
更新:
使用这个伟大的帖子,我已经改变了我的扩展类如下:
public static void ObserveGrid(this Form form, string controlName, Action<object,object> action)
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0] as DataGridView;
if (control != null)
{
EventInfo ei = typeof(DataGridView).GetEvent("DataSourceChanged");
if (ei != null)
{
Delegate handler = ConvertDelegate(action, ei.EventHandlerType);
ei.AddEventHandler(control, handler);
}
}
}
}
public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(
targetDelegateType,
originalDelegate.Target,
originalDelegate.Method);
}
然而,我得到了另一个错误,这次是关于从非同步线程释放互斥锁:
释放互斥锁。System.Reflection.TargetInvocationException:异常已被调用的目标抛出。——>系统。ApplicationException:对象同步方法从未同步的代码块中调用。
更新2
为SemaphoreSlim交换互斥锁解决了同步问题。
我最后是这样做的:
首先,Extensions类:public static class Extensions
{
#region Public Methods and Operators
public static Stopwatch ClickButton(this Form form, string controlName)
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0] as Button;
if (control != null)
{
MethodInfo mi = typeof(Button).GetMethod("OnClick", BindingFlags.NonPublic | BindingFlags.Instance);
var stopWatch = Stopwatch.StartNew();
mi.Invoke(
control,
new object[]
{
EventArgs.Empty
});
return stopWatch;
}
}
throw new ApplicationException("Control not found or of invalid Type");
}
public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(targetDelegateType, originalDelegate.Target, originalDelegate.Method);
}
public static object GetControlProperty<T>(this Form form, string controlName, string propertyName) where T : class
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0];
PropertyInfo pi = typeof(T).GetProperty(propertyName);
return pi.GetValue(control, null);
}
throw new ApplicationException("Control not found or of invalid Type");
}
public static void ObserveControlEvents<T>(this Form form, string controlName, string eventName, Action<object, object> action) where T : class
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0] as T;
if (control != null)
{
EventInfo ei = typeof(T).GetEvent(eventName);
if (ei != null)
{
Delegate handler = ConvertDelegate(action, ei.EventHandlerType);
ei.AddEventHandler(control, handler);
}
}
}
}
public static void ObserveGrid(this Form form, string controlName, string eventName, Action<object, object> action)
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0] as DataGridView;
if (control != null)
{
EventInfo ei = typeof(DataGridView).GetEvent(eventName);
if (ei != null)
{
Delegate handler = ConvertDelegate(action, ei.EventHandlerType);
ei.AddEventHandler(control, handler);
}
}
}
}
public static void SetControlProperty<T>(this Form form, string controlName, string propertyName, object value) where T : class
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0];
PropertyInfo pi = typeof(T).GetProperty(propertyName);
pi.SetValue(control, value, null);
}
}
public static void SetFormProperty(this Form form, string controlName, object value)
{
var controls = form.Controls.Find(controlName, true);
if (controls.Any())
{
var control = controls[0];
PropertyInfo pi = typeof(Control).GetProperty("Text");
pi.SetValue(control, value, null);
}
}
#endregion
}
现在,在我的单元测试中,我可以创建表单,设置值,触发和观察事件:
[TestFixture]
public class FormsTests
{
#region Public Methods and Operators
[TestCase(null, null, null, 1000)]
[TestCase("Kim", null, null, 500)]
[TestCase("Kim", null, "Akers", 250)]
[TestCase("Kim", "B", "Abercrombie", 100)]
public void InsuredSearcherResponseTimeWithReflectionTest(string firstName, string middleName, string lastName, long milliseconds)
{
var monitor = new SemaphoreSlim(1);
monitor.Wait();
var form = new Insured();
form.SetControlProperty<TextBox>("tbFirstName", "Text", firstName);
form.SetControlProperty<TextBox>("tbMiddleName", "Text", middleName);
form.SetControlProperty<TextBox>("tbLastName", "Text", lastName);
form.ObserveControlEvents<DataGridView>(
"dataGridView1",
"DataSourceChanged",
(sender, args) =>
{
Trace.WriteLine("Occured in delegate");
monitor.Release();
});
Trace.WriteLine("Executing");
var sw = form.ClickButton("btnSearch");
monitor.Wait();
sw.Stop();
Trace.WriteLine(String.Format("Row count was {0} took {1}ms to process", form.GetControlProperty<DataGridView>("dataGridView1", "RowCount"), sw.ElapsedMilliseconds));
Assert.IsTrue(sw.ElapsedMilliseconds < milliseconds);
}
[TestFixtureSetUp]
public void TestFixtureSetup()
{
var monitor = new SemaphoreSlim(1);
monitor.Wait();
var form = new Insured();
form.ObserveControlEvents<DataGridView>(
"dataGridView1",
"DataSourceChanged",
(sender, args) =>
{
Trace.WriteLine("Occured in delegate");
monitor.Release();
});
form.ClickButton("btnSearch");
monitor.Wait();
}
}
我希望这也能帮助到别人。
试试下面的定义:
public static class Xtd
{
public static void AddEventEasy(this object component, EventInfo eventInfo, Delegate eventAction)
{
Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(
targetDelegateType,
originalDelegate.Target,
originalDelegate.Method);
}
eventInfo.AddEventHandler(component, ConvertDelegate(eventAction, eventInfo.EventHandlerType));
}
}