簡體   English   中英

如何將事件處理程序委托轉換為具有不同簽名的委托

[英]How do I cast an event handler delegate to one with a different signature

我編寫的代碼實際上是WPF行為,用於從網格控件中獲取選定的項(眾所周知,SelectedItems不是可綁定的屬性)。 我實際上正在使用Telerik RadGridView,但是我希望該行為對於帶有SelectionChanged事件的任何事物都是通用的。 但是,不同的控件對SelectionChanged事件處理程序具有不同的簽名(RadGridView使用Telerik.Windows.Controls.SelectionChangeEventArgs,而標准GridView使用System.Windows.Controls.SelectionChangedEventArgs)。 我們可以確定的一件事是,事件args將從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]'。”}

在System.RuntimeType.TryChangeType處(對象值,活頁夾活頁夾,CultureInfo文化,布爾布爾NeedSpecialCast)

有人可以建議嗎?

調用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);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM