Поиск событий через отражение C#
У меня есть проблема, касающаяся событий и отражения в C#. В настоящее время я программирую с помощью Microsoft Kinect (SDK 1.7) и хочу реализовать щелчок, отличный от подхода "Push-To-Press". Сам ClickEvent объявлен в классе KinectControl.
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);
}
}
}
Элемент управления, который должен вызывать этот ClickEvent и вызывать соответствующий метод, является объектом "Menu_Point", который наследуется от класса KinectControl.
После распознавания жеста щелчка объект "source" и соответственно "Menu_Point" вызывает ClickEvent следующим образом:
invokeCtrl.Raise<RoutedEventArgs>("Click", new RoutedEventArgs(KinectControl.ClickEvent));
который вызывает следующий метод:
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 });
}
}
}
Он работает нормально, пока вызывается метод Raise и попадает в эту строку:FieldInfo fieldInfo =
source.GetType().GetField(eventName, BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static |
BindingFlags.GetField);
Я не знаю почему, но поле информации всегда остается null
что вызывает исключение, если eventDelegate
пытается получить к нему доступ. К сожалению, я не могу найти причину, почему ClickEvent не найден в этом случае. Член eventName
имеет значение "Click", которое является именем события в KinectControl-классе. Я также искал ответы на этом сайте (но ни одно из решений не помогло мне до сих пор), и я прочитал все виды блогов, таких как DotNetPearls.com, но все же я не могу найти ошибку здесь. Я надеюсь, что вы можете помочь мне. Заранее спасибо!
Редактировать: добавлено BindingFlags.Static
; забыл, что в FieldInfo. Спасибо, Майкл Гюнтер. Тем не менее, не работает, хотя.
2 ответа
Было бы лучше, если бы вы просто призвать UIElement.RaiseEvent
- RoutedEvents не нужно иметь фактическую реализацию EventHandler - поэтому они просто делегируют эту работу UIElement.AddHandler
а также UIElement.RemoveHandler
,
Сказав это - вот как вы, скорее всего, найдете событие:
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));
}
}
Надеюсь это поможет!
Это похоже на работу:
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 });