2
votes

I have a problem concerning Events and Reflection in C#. I'm currently programming with the Microsoft Kinect (SDK 1.7) and want to implement a different click than the "Push-To-Press"-Approach. The ClickEvent itself is declared in the KinectControl class.

    public partial class KinectControl : UserControl
    {
        /**
        \brief Default Click event 
        */

        public static readonly RoutedEvent ClickEvent =
            EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(KinectControl)); 
        [...]

        public event RoutedEventHandler Click
        {
            add
            {
                AddHandler(ClickEvent, value);
            }
            remove
            {
                RemoveHandler(ClickEvent, value);
            }
        }
    } 

The Control which should raise this ClickEvent and invoke the proper method is a "Menu_Point"-object which inherits from the KinectControl-class.

After recognizing a click gesture, the "source" respectively "Menu_Point"-object raises the ClickEvent like this:

invokeCtrl.Raise<RoutedEventArgs>("Click", new RoutedEventArgs(KinectControl.ClickEvent));

which calls the following method:

        public static void Raise<TEventArgs>(this object source, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs
    {
        var list = 
            source.GetType().GetFields(BindingFlags.Public | 
            BindingFlags.NonPublic | BindingFlags.Instance | 
            BindingFlags.Static | BindingFlags.SetField | 
            BindingFlags.InvokeMethod | BindingFlags.Instance);
        FieldInfo fieldInfo = 
            source.GetType().GetField(eventName, BindingFlags.Public | 
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static |
            BindingFlags.GetField);

        var eventDelegate = 
            (MulticastDelegate)fieldInfo.GetValue(source);

        if (eventDelegate != null)
        {
            foreach (var handler in eventDelegate.GetInvocationList())
            {
                handler.Method.Invoke(handler.Target, new object[]{ source, eventArgs });
            }
        }
    }

It works fine intil the Raise-method is called and gets to this line: FieldInfo fieldInfo = source.GetType().GetField(eventName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.GetField); I don't know why, but the fieldInfo ist always remaining null which causes an exception if eventDelegate is trying to get access to it. Unfortunately I'm not able to find the reason why the ClickEvent is not found in this case. The member eventName has the value "Click" which is the name of the Event in the KinectControl-class. I also searched this site for answers (but none of the solutions helped me so far) and I read all kinds of regarding blogs like DotNetPearls.com but still I can't seem to find the mistake here. I hope you can help me out. Thanks in advance!

Edit: added BindingFlags.Static; forgot that in the FieldInfo. Thanks @Michael Gunter. Still doesn't work, though.

2
The AddHandler() method here is likely to be the DependencyObject.AddHandler() method. Which stores the event in an internal Dictionary in a field named "dependencyPropertyValues". You'll have to dig that out with reflection. This is not supposed to be easy.Hans Passant
Doesn't sound easy, indeed. Luckily, the solution of @Andreas Prechtl worked for me. The method goes through (I think) all events of all Framework elements and finds the "Click" event eventually.Crash

2 Answers

0
votes

It would be better if you just invoke UIElement.RaiseEvent - RoutedEvents don't need to have an actual EventHandler implementation - that's why they simply delegate that work to UIElement.AddHandlerand UIElement.RemoveHandler.

Having said that - here's how you'll most likely find the event:

    public static RoutedEvent GetEvent(this UIElement source, string eventName)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        if (string.IsNullOrEmpty(eventName))
            throw new ArgumentException("eventName");

        // check if the type is directly in there, otherwise you need a second pass and a more general approach
        RoutedEvent routedEvent = null;

        // this may return null...
        RoutedEvent[] events = EventManager.GetRoutedEventsForOwner(source.GetType());

        if (events != null)
            routedEvent = events.FirstOrDefault(p => p.Name == eventName);

        return routedEvent ?? EventManager.GetRoutedEvents().FirstOrDefault(p => p.OwnerType.IsInstanceOfType(source) && p.Name == eventName);
    }

    public static void RaiseEvent(this UIElement source, string eventName)
    {
        RoutedEvent routedEvent = GetEvent(source, eventName);

        if (routedEvent != null)
            source.RaiseEvent(new RoutedEventArgs(routedEvent, source));
    }
}

Hope this helps!

0
votes

This seems to work:

var eventField = source.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
var eventDelegate = eventField.GetValue(source) as MulticastDelegate;
if (eventDelegate != null)
    eventDelegate.DynamicInvoke(new object[] { source, eventArgs });