2014-09-11 47 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