如何将事件处理程序委托强制转换为具有不同签名的委托
本文关键字:事件处理 程序 转换 | 更新日期: 2023-09-27 18:21:41
我正在编写的代码实际上是一种WPF行为,用于从网格控件中获取所选项(众所周知,SelectedItems不是可绑定属性)。事实上,我使用的是Telerik RadGridView,但我希望行为对于SelectionChanged事件来说是通用的。然而不同的控件对SelectionChanged事件处理程序有不同的签名(RadGridView使用Telerik.Windows.controls.SelectionChangeEventArgs,而标准GridView使用System.Windows.controls.SelectionChangedEventArgs)。我们可以确定的一件事是,事件args将从EventArgs派生(事实上,我们可以确定它将从RoutedEventArg派生)。
然而,虽然我可以编写一个以RoutedEventArgs作为第二个参数的通用事件处理程序,并且我可以使用反射来获取SelectionChangedEvent的EventInfo,但如果不使用事件处理程序的精确签名,我就无法将处理程序挂接到事件-在本例中是RadGridView处理程序。
这是我的密码。我已经包含了所有内容,但重要的一点是SelectItemPropertyChanged,它是DependencyObject PropertyChangedCallback,试图将事件处理程序SelectionChangedHandler连接到SelectionChangedPEvent。(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<Telerik.Windows.Controls.SelectionChangeEventArgs>)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<Telerik.Windows.Controls.SelectionChangeEventArgs>)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;
}
}
我已经尝试了各种方法来使用反射为处理程序获取正确的签名,从而为正确的类型创建委托,但我就是无法使其工作——AddEventHandler(和RemoveEventHandler)抛出InvalidArgumentException,完整堆栈跟踪如下:
{"无法将类型为System.Windows.RoutedEventHandler的对象转换为类型为System.EventHandler `1[Telerik.Windows.Controls.SelectionChangeEventArgs]。"}
在System.RuntimeType.TryChangeType(对象值,活页夹,CultureInfo区域性,布尔需求SpecialCast)
有人能提出建议吗?
调用AddEventHandler
时,需要将委托转换为事件的EventHandlerType
。下面是一个示例应用程序:
using System;
using System.Reflection;
using System.Threading;
namespace App
{
class Program
{
public event EventHandler<ThreadExceptionEventArgs> 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);
}
}
}