如何验证是否已添加事件处理程序

时间:2014-07-24 09:57:39

标签: c# wpf events event-handling

我在一个UIElement中添加了一个EventHandler:

element.AddHandler(Class.Event, handler, true);

但是没有调用处理程序方法。如何验证处理程序是否已正确添加?类似的东西:

element.GetAssociatedHandlers();

谢谢!

2 个答案:

答案 0 :(得分:3)

如果您只想在调试器中验证添加了事件处理程序,可以将事件转换为System.Delegate并查看调用列表。例如,可以在Visual Studio立即窗口中完成此操作。在这里,我正在检查名为“MyEvent”的事件的调用列表,该事件最终增加了3个处理程序:

((System.Delegate)MyEvent).GetInvocationList()
{System.Delegate[3]}
    [0]: {Method = {Void handler11_MessageReceived(System.Object, MyProject.MyEventArgs)}}
    [1]: {Method = {Void handler21_MessageReceived(System.Object, MyProject.MyEventArgs)}}
    [2]: {Method = {Void handler21_MessageReceived(System.Object, MyProject.MyEventArgs)}}

您还可以在Watch窗口中浏览调用列表。

如果你不确定某些事件监听器是否在以后被妥善处理,那么这样做甚至可能是有用的。

更新:如何以编程方式检查一个事件是否包含另一个事件。

以编程方式检查事件处理程序是否已添加到事件中比人们想象的更复杂,因为事件实际上是MulticastDelegate的一种形式。多播委托可以是由(例如)delegate语句或lambda表达式创建的“原子”删除,或者是通过将两个或更多个多播委托加在一起而创建的组合委托。事实证明,当一个组合代表被添加到第二个或从中减去时,实际发生的是它的调用列表被添加到另一个或从另一个减去。 E.g。

        Action a = () => Console.WriteLine("a");
        Action b = () => Console.WriteLine("b");
        Action c = () => Console.WriteLine("c");
        Action d = () => Console.WriteLine("d");

        var ab = a + b;
        var cd = c + d;
        var ad = a + d;
        var bc = b + c;

        var abcd = ab + cd;
        var adbc = ad + bc;
        var abc = abcd - d;
        var bcd = abcd - a;

        bool test1 = (abcd == (a + b + c + d)); // returns true
        bool test2 = abcd.GetInvocationList().Contains(a); // returns true;
        bool test3 = abcd.GetInvocationList().Contains(ab); // returns **false**;
        bool test4 = abc.GetInvocationList().Contains(d); // returns false

如果要创建公共静态扩展方法以检查您的处理程序是否已添加到事件中,它应该正确处理添加的多播委托的情况。但是,这只是意味着事件具有处理程序的所有委托吗?或者它们是否需要按顺序绑定在一起,因为Delegate.Combine确实保留了顺序?以下扩展方法检查后者:

public static class EventHelper
{
    /// <summary>
    /// Return true if all the atomic delegates in the multicast delegate handler are bound into the
    /// publisher, grouped together and in the same order.
    /// </summary>
    /// <param name="publisher"></param>
    /// <param name="handler"></param>
    /// <returns></returns>
    public static bool HasBound(this Delegate publisher, Delegate handler)
    {
        if (publisher == null || handler == null)
            return false;
        if (publisher == handler)
            return true;
        var publisherList = publisher.GetInvocationList();
        var handlerList = handler.GetInvocationList();
        return (publisherList.SublistIndex(handlerList, 0) >= 0);
    }

    public static bool HasBound<TEventArgs>(this EventHandler<TEventArgs> publisher, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
    {
        return HasBound((Delegate)publisher, (Delegate)handler);
    }

    public static bool HasBound(this EventHandler publisher, EventHandler handler)
    {
        return HasBound((Delegate)publisher, (Delegate)handler);
    }
}

public static class ListHelper
{
    public static int SublistIndex<T>(this IList<T> list, IList<T> sublist, int start)
    {
        var comparer = EqualityComparer<T>.Default;
        for (int listIndex = start, end = list.Count - sublist.Count + 1; listIndex < end; listIndex++)
        {
            int count = 0;
            while (count < sublist.Count && comparer.Equals(sublist[count], list[listIndex + count]))
                count++;
            if (count == sublist.Count)
                return listIndex;
        }
        return -1;
    }
}

以下是测试结果:

        bool test08 = a.HasBound(a); // returns true;
        bool test09 = b.HasBound(a); // returns true;
        bool test10 = abcd.HasBound(a + b + c + d); // returns true;
        bool test11 = abcd.HasBound(adbc); // returns false - wrong order.
        bool test12 = abcd.HasBound(a); // returns true;
        bool test13 = cd.HasBound(a); // return false
        bool test14 = bcd.HasBound(bc); // returns true despite the fact that bcd was not created directly from bc.
        bool test15 = abcd.HasBound(ad); // returns false because the "ad" events are not adjacent in abcd.

老实说,我不会在调试之外这样做。实际上编写一个检查以查看监听器是否绑定到某个事件似乎是错误的。

更新2 这里真正的问题是如何从Microsoft UIElement获取所有事件处理程序?它必须通过一些令人讨厌的反映来完成,这些反映不能保证在将来的.Net版本中工作,如下所示:How to remove all Click event handlers?和此处:How would it be possible to remove all event handlers of the 'Click' event of a 'Button'?

答案 1 :(得分:0)

您可以获取委托的调用列表以验证您的处理程序是否已附加。

event Action MyEvent;
bool IsEventHandlerAdded()
{
    foreach (Delegate existingHandler in this.MyEvent.GetInvocationList())
    {
        return existingHandler == MyEvent;
    }

    return false;
}