如何使用反射将匿名操作绑定为新事件处理程序

时间:2014-05-14 15:16:58

标签: c# winforms events reflection datagridview

我正在为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();

执行期间我收到错误:

  

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

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

更新

使用this精彩帖子,我已经更改了我的扩展程序类:

    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:   调用的目标抛出了异常。    ----&GT; System.ApplicationException:   从不同步的代码块调用对象同步方法。

更新2

交换SemaphoreSlim的Mutex解决了同步问题。

2 个答案:

答案 0 :(得分:1)

这是我最终做的事情:

首先,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();
    }
}

我希望这对某人也有帮助。

答案 1 :(得分:0)

尝试以下定义:

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