如何将事件处理程序委托转换为具有不同签名的委托

我写的代码实际上是一个WPF行为,用于从网格控件中获取所选项(SelectedItems,我们知道,它不是一个可绑定的属性)。 我实际上正在使用Telerik RadGridView但我希望行为对于任何具有SelectionChanged事件的内容都是通用的。 但是,不同的控件具有SelectionChanged事件处理程序的不同签名(RadGridView使用Telerik.Windows.Controls.SelectionChangeEventArgs而标准GridView使用System.Windows.Controls.SelectionChangedEventArgs)。 我们可以肯定的一件事是事件args将派生自EventArgs(实际上我们可以确定它将从RoutedEventArgs派生)。

但是,虽然我可以编写一个通用事件处理程序,它将RoutedEventArgs作为其第二个参数,并且我可以使用reflection来获取SelectionChangedEvent的EventInfo,但是我不能在不使用事件的精确签名的情况下将处理程序挂钩到事件handler – 在本例中是RadGridView处理程序。

这是我的代码。 我已经包含了所有内容,但重要的是SelectItemPropertyChanged,它是DependencyObject PropertyChangedCallback,它尝试将事件处理程序SelectionChangedHandler连接到SelectionChangedEvent。 (SelectionChangedHandler中的代码与问题无关,但我已经把它留在了,所以我很清楚我在做什么)。

public static class SelectedItemsChangedBehaviour{ public static readonly DependencyProperty SelectItemsProperty = DependencyProperty.RegisterAttached("SelectItems", typeof(bool), typeof(SelectedItemsChangedBehaviour), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(SelectItemPropertyChanged))); public static void SetSelectItems(DependencyObject dependencyObject, bool selectItems) { dependencyObject.SetValue(SelectItemsProperty, selectItems); } public static bool GetSelectItems(DependencyObject dependencyObject) { return (bool)dependencyObject.GetValue(SelectItemsProperty); } private static void SelectItemPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { // No common base for all classes with SelectionChanged event so use reflection EventInfo selectionChangedEventInfo = dependencyObject.GetType().GetEvent("SelectionChanged"); if (selectionChangedEventInfo == null) { throw new ArgumentException("Must have a SelectionChanged event."); } if ((bool)dependencyPropertyChangedEventArgs.OldValue) { // This is what I want to do, but it throws because the handler signature is wrong selectionChangedEventInfo.RemoveEventHandler(dependencyObject, (RoutedEventHandler)SelectionChangedHandler); // This works fine because it is of the right type for the RadGridView but is not general //selectionChangedEventInfo.RemoveEventHandler(dependencyObject, (EventHandler)SelectionChangedHandler); } if ((bool)dependencyPropertyChangedEventArgs.NewValue) { // This is what I want to do, but it throws because the handler signature is wrong selectionChangedEventInfo.AddEventHandler(dependencyObject, (RoutedEventHandler)SelectionChangedHandler); // This works fine because it is of the right type for the RadGridView but is not general //selectionChangedEventInfo.AddEventHandler(dependencyObject, (EventHandler)SelectionChangedHandler); } } private static void SelectionChangedHandler(object sender, RoutedEventArgs eventArgs) { // No common base for all classes with AddedItems/RemovedItems (eg. System.Windows.Controls.SelectionChangedEventArgs / Telerik.Windows.Controls.SelectionChangeEventArgs PropertyInfo addedItemsInfo = eventArgs.GetType().GetProperty("AddedItems"); PropertyInfo removedItemsInfo = eventArgs.GetType().GetProperty("RemovedItems"); if (addedItemsInfo == null || removedItemsInfo == null) { throw new ArgumentException("Must have AddedItems and RemovedItems"); } foreach (object item in (IEnumerable)addedItemsInfo.GetValue(eventArgs, null)) { ((ISelectable)item).IsSelected = true; } foreach (object item in (IEnumerable)removedItemsInfo.GetValue(eventArgs, null)) { ((ISelectable)item).IsSelected = false; } } 

我已经尝试了各种方法来使用reflection来获取处理程序的正确签名,从而创建一个到正确类型的委托,但我无法使其工作 – AddEventHandler(和RemoveEventHandler)抛出InvalidArgumentException,完整堆栈跟踪如下:

{“System.Windows.RoutedEventHandler”类型的对象无法转换为类型’System.EventHandler`1 [Telerik.Windows.Controls.SelectionChangeEventArgs]’。“}

at System.RuntimeType.TryChangeType(Object value,Binder binder,CultureInfo culture,Boolean needsSpecialCast)

任何人都可以建议吗?

调用AddEventHandler时,需要将委托转换为事件的EventHandlerType 。 这是一个示例应用程序:

 using System; using System.Reflection; using System.Threading; namespace App { class Program { public event EventHandler E; static void Main () { new Program().Run(); } private void Run () { EventInfo e = typeof(Program).GetEvent("E"); EventHandler untypedHandler = OnE; Delegate typedHandler = Delegate.CreateDelegate(e.EventHandlerType, untypedHandler.Target, untypedHandler.Method); e.AddEventHandler(this, typedHandler); E(this, new ThreadExceptionEventArgs(new Exception("Hello world!"))); } private void OnE (object sender, EventArgs args) { Console.WriteLine(((ThreadExceptionEventArgs)args).Exception.Message); } } }