如何使用反射将匿名操作绑定为新的事件处理程序 [英] How do I bind an Anonymous action as new event handler using reflection

查看:144
本文介绍了如何使用反射将匿名操作绑定为新的事件处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为Windows窗体编写一些单元测试,到目前为止,已经能够使用Reflection来了解如何设置私有控件的属性并调用它们的方法。但是我坚持如何将一个内联的lambda关联到一个控件上发生的事件,在这种情况下DataGridView的DataSourceChanged事件。

  public static void ObserveGrid(此Form窗体,字符串controlName,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(释放互斥体);
monitor.ReleaseMutex() ;
});
var sw = new Stopwatch();
form.ClickButton(btnSearch,sw);
monitor.WaitOne();
sw.Stop();

执行期间我收到错误:


无法绑定到目标方法,因为其签名或安全性
透明度与委托类型的兼容性不兼容。


在这种情况下,我做错了什么?



更新:



使用伟大的帖子,我已经改变了我的扩展类,如下所示:

  public static void ObserveGrid(此表单,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)
{
委托处理程序= 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:
异常被调用的目标抛出。
----> System.ApplicationException:
从不同步的代码块调用对象同步方法。


更新2



为SemaphoreSql交换Mutex解决了同步问题。

解决方案

这是我最后做的:



扩展类:

  public static class Extensions 
{
#region公共方法和运算符

public static秒表ClickButton(此表单,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 the invalid Type);
}

public static Delegate ConvertDelegate(Delegate originalDelegate,Type targetDelegateType)
{
return Delegate.CreateDelegate(targetDelegateType,originalDelegate.Target,originalDelegate.Method);
}

public static object GetControlProperty< T>(此表单,string controlName,string propertyName)其中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 the invalid Type);
}

public static void ObserveControlEvents< T>(此Form窗体,字符串controlName,string eventName,Action< object,object> action)其中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)
{
委托处理程序= ConvertDelegate(action,ei.EventHandlerType);
ei.AddEventHandler(control,handler);
}
}
}
}

public static void ObserveGrid(此Form窗体,字符串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)
{
委托处理程序= ConvertDelegate(action,ei.EventHandlerType);
ei.AddEventHandler(control,handler);
}
}
}
}

public static void SetControlProperty< T>(此Form窗体,字符串controlName,字符串propertyName,对象值) 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(此Form窗体,字符串controlName,对象值)
{
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公共方法和运算符

[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)=>
{
跟踪。 WriteLine(出现在委托);
monitor.Release();
});
Trace.WriteLine(Executing);
var sw = form.ClickButton(btnSearch);
monitor.Wait();
sw.Stop();
Trace.WriteLine(String.Format(Row count was {0} take {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)=>
{
跟踪。 WriteLine(出现在委托);
monitor.Release();
});
form.ClickButton(btnSearch);
monitor.Wait();
}
}

我希望这有助于某人。 >

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:

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
}

Now, in my unit tests I can create the form, set values, trigger and observe the events:

[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();
    }
}

I hope this helps someone as well.

这篇关于如何使用反射将匿名操作绑定为新的事件处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆