I am writing a number of unit tests for Windows Forms, and so far have been able to figure out how to set private controls' properties and invoke their methods, using Reflection. But I am stuck on how to associate an in-line lambda to attach itself to an event occurring on one of the controls, in this case the DataSourceChanged event of DataGridView.
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 ));
}
}
}
}
I was hoping to call it like this:
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();
During execution I get an error:
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
What am I doing wrong in this case?
UPDATE:
Using this great post, I have changed my Extensions class like so:
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);
}
However I get another error, this time about releasing the mutex from an non-synchronized thread:
Releasing mutex. System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation. ----> System.ApplicationException : Object synchronization method was called from an unsynchronized block of code.
UPDATE 2
Swapping Mutex for SemaphoreSlim resolved the synchronization issue.
Here is what I ended up doing:
First, the Extensions class:
Now, in my unit tests I can create the form, set values, trigger and observe the events:
I hope this helps someone as well.