2014-09-11 16 views
3

我正在编写的代码实际上是一种WPF行为,用于从网格控件(SelectedItems,因为我们知道,它不是可绑定属性)中获取所选项目。我实际上使用Telerik RadGridView,但我希望行为对于任何具有SelectionChanged事件的事件来说都是通用的。但是,不同的控件对SelectionChanged事件处理程序具有不同的签名(RadGridView使用Telerik.Windows.Controls.SelectionChangeEventArgs,而标准的GridView使用System.Windows.Controls.SelectionChangedEventArgs)。我们可以确定的一件事是事件参数将来自EventArgs(实际上我们可以确定它将从RoutedEventArgs派生)。然而,虽然我可以编写一个将RoutedEventArgs作为其第二个参数的常规事件处理程序,但是我可以使用反射来获取SelectionChangedEvent的EventInfo,但是我无法使用精确性将该处理程序挂接到该事件事件处理程序的签名 - 在本例中为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<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]’ 。“}

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

有谁能提醒?

+0

你说“它抛出” - 请你可以给完整的堆栈跟踪? (或者如果这是一个编译时错误,那么给出确切的错误信息 - “throws”在这种情况下并不合适)。如果你可以将重要的部分提取到一个简短但完整的程序中 - 它不需要是WPF。 – 2014-09-11 12:10:08

+0

它抛出(我不会说如果它是一个编译器错误抛出:-)) 例外是: System.ArgumentException {“对象的类型'System.Windows.RoutedEventHandler'不能转换为类型' System.EventHandler'1 [Telerik.Windows.Controls.SelectionChangeEventArgs]'。“} at System.RuntimeType.TryChangeType(Object value,Binder binder,CultureInfo culture,Boolean needsSpecialCast) 我会试着把它放到程序中,但正如我所说的重要的是SelectItemPropertyChanged – Dave 2014-09-11 12:46:52

+0

(我如何把这些注释新行????) – Dave 2014-09-11 12:52:40

回答

2

调用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); 
     } 
    } 
} 
+0

Athari。我知道它说不要在评论中写下“谢谢”,但是无论如何感谢,这是一种享受。 – Dave 2014-09-11 13:44:14

+0

这有一个潜在的问题 - 如果你想在另一个方法中删除事件处理程序。我从来没有完全理解C#如何在事件处理程序上定义相等性,但我确实知道,如果您尝试删除一个,并且它不等于先前添加的那个,那么它不会被删除。我*认为*解决办法是让typedHandler成为一个成员字段,但我会对其他任何意见感兴趣。 – Dave 2014-09-11 14:01:39

+0

@Dave是的,你需要存储'typedHandler'才能稍后退订。顺便提一下,你也可以提出答案。 – Athari 2014-09-11 14:52:21