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.EventHandler
1[System.Windows.RoutedPropertyChangedEventArgs
1[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; }
}