Home > Software design >  Passing arguments to RoutedEventHandler
Passing arguments to RoutedEventHandler

Time:11-25

I have a UserControl with a Slider which I'd like to handle RoutedEvent. Button was no problem and it works fine, since there are no args. This is my Button handler:

public static readonly RoutedEvent OnOffClickEvent = EventManager.RegisterRoutedEvent(
     nameof(OnOffClick), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ColorLightControl));
public event RoutedEventHandler OnOffClick
{
     add => AddHandler(OnOffClickEvent, value);
     remove => RemoveHandler(OnOffClickEvent, value);
}

private void ButtonOnOff_Click(object sender, RoutedEventArgs e) =>
     RaiseEvent(new RoutedEventArgs(OnOffClickEvent));

But banging my head on the table because can't handle Slider's ValueChanged event. This is what I coded but it doesn't work on execution.

public static readonly RoutedEvent ColorSlideEvent = EventManager.RegisterRoutedEvent(
     nameof(ColorSlide), RoutingStrategy.Bubble, typeof(EventHandler<RoutedPropertyChangedEventArgs<double>>), typeof(ColorLightControl));
public event EventHandler<RoutedPropertyChangedEventArgs<double>> ColorSlide
{
     add => AddHandler(ColorSlideEvent, value);
     remove => RemoveHandler(ColorSlideEvent, value);
}

private void SliderColor_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) =>
     RaiseEvent(new RoutedPropertyChangedEventArgs<double>(e.OldValue, e.NewValue, ColorSlideEvent));

I get this error and I'm pretty sure it's because ColorSlide is not declated as RoutedEventHandler but not sure how to pass args. Any help appreciated. Thanks

System.InvalidCastException: 'Unable to cast object of type 'System.EventHandler1[System.Windows.RoutedPropertyChangedEventArgs1[System.Double]]' to type 'System.Windows.RoutedPropertyChangedEventHandler`1[System.Double]'.'

CodePudding user response:

The error message tells the whole story:
either use the appropriate event handler RoutedPropertyChangedEventHandler (instead of EventHadler<T>) or create a custom class that extends RoutedEventArgs.

The reason is that RoutedPropertyChangedEventArgs<T> overrides the virtual RoutedEventArgs.InvokeEventHandler method (which is highly recommended to do so). In this override you usually cast the Delegate to a specific type (in place of reflection). This is where the coupling between RoutedPropertyChangedEventArgs and RoutedPropertyChangedEventHandler is created and where the exception you have encountered is thrown (as a result of the failed explicit type cast).
Since the default implementation of RoutedEventArgs.InvokeEventHandler uses reflection, you are advised to provide a specialized override to improve the performance (as you eliminate the reflection required to write a generic event invocator).

An example to create a custom routed event args type including the recommended RoutedEventArgs.InvokeEventHandler override:

// Optional delegate as an alternative to 'EventHandler<SliderValueChangedRoutedEventArgs<TValue>>'.
public delegate void SliderValueChangedRoutedEventHandler<TValue>(object sender, SliderValueChangedRoutedEventArgs<TValue> e);

// Routed event args that supports the 'SliderValueChangedRoutedEventHandler<TValue>' delegate
// and the 'EventHandler<SliderValueChangedRoutedEventArgs<TValue>>' delegate
public class SliderValueChangedRoutedEventArgs<TValue> : RoutedEventArgs
{
  public SliderValueChangedRoutedEventArgs()
  {
  }

  public SliderValueChangedRoutedEventArgs(RoutedEvent routedEvent) : base(routedEvent)
  {
  }

  public SliderValueChangedRoutedEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source)
  {
  }

  public SliderValueChangedRoutedEventArgs(RoutedEvent routedEvent, object source, TValue value) : base(routedEvent, source)
  {
    this.Value = value;
  }

  protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget)
  {
    if (genericHandler is EventHandler<SliderValueChangedRoutedEventArgs<TData>> defaultHandler)
    {
      defaultHandler.Invoke(genericTarget, this);
    }
    else
    {
      var strongTypedHandler = (SliderValueChangedRoutedEventHandler<TData>)genericHandler;
      strongTypedHandler.Invoke(genericTarget, this);
    }
  }

  public TValue Value { get; }
}
  • Related