Search code examples
wpfmvvmrouted-events

How to bind a custom routed event to a command in the view model?


I am trying to define a custom routed event and then bind this routed event to a command in my view model.

The problem is that I get the exception: "InvalidCastException: Unable to cast object of type 'System.Reflection.RuntimeEventInfo' to type 'System.Reflection.MethodInfo'."

The code behind in my user control that defines the custom routed event:

public static readonly RoutedEvent ItemDobleClickEvent = EventManager.RegisterRoutedEvent(
    "ItemDobleClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyUSerControl));

// Provide CLR accessors for the event
public event RoutedEventHandler ItemDobleClick
{
    add { AddHandler(CItemDobleClickEvent, value); }
    remove { RemoveHandler(ItemDobleClickEvent, value); }
}


void RaiseItemDobleClickEvent(MyType? paramItem)
{
    // Create a RoutedEventArgs instance.
    RoutedEventArgs routedEventArgs = new(routedEvent: ItemDobleClickEvent);

    // Raise the event, which will bubble up through the element tree.
    RaiseEvent(routedEventArgs);
}

This is the view of the main view, that use the user control:

<local:ucComponentesBaseView x:Name="MyControl" Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                                ItemDobleClick="{Binding ItemDobleClickCommand}"/>

This is the code in my main view model:

private RelayCommand? _itemDobleClickCommand;
public RelayCommand ItemDobleClickCommand
{
    get { return _itemDobleClickCommand ?? (_itemDobleClickCommand = new RelayCommand(param => ItemDobleClickCommandHandler(), param => true)); }
}


private void ItemDobleClickCommandHandler()
{
    //TODO
}

I am biding the rest of the commands in this way.

How could I bind a custom routed event to the command in my view model?

Thanks.


Solution

  • You can implement EventToCommandBehavior on your own:

    using System.Windows;
    using System.Windows.Input;
    using Microsoft.Xaml.Behaviors;
    
    namespace WpfApplication1.Behaviors
    {
        public class EventToCommandBehavior : Behavior<FrameworkElement>
        {
            public static readonly DependencyProperty RoutedEventProperty = DependencyProperty.Register(
                nameof(RoutedEvent),
                typeof(RoutedEvent),
                typeof(EventToCommandBehavior),
                new PropertyMetadata(null));
    
            public RoutedEvent RoutedEvent
            {
                get => (RoutedEvent)GetValue(RoutedEventProperty);
                set => SetValue(RoutedEventProperty, value);
            }
            
            public static readonly DependencyProperty WithCommandProperty = DependencyProperty.Register(
                nameof(WithCommand),
                typeof(ICommand),
                typeof(EventToCommandBehavior),
                new PropertyMetadata(null));
    
            public ICommand WithCommand
            {
                get => (ICommand)GetValue(WithCommandProperty);
                set => SetValue(WithCommandProperty, value);
            }
            
            readonly RoutedEventHandler _handler;
    
            public EventToCommandBehavior()
            {
                _handler = (s, e) =>
                {
                   var args = e.OriginalSource;
                    
                    if (WithCommand.CanExecute(args))
                    {
                        WithCommand.Execute(args);
                    }
                };
            }
            
            protected override void OnAttached()
            {
                base.OnAttached();
                AssociatedObject.AddHandler(RoutedEvent, _handler);
            }
    
            protected override void OnDetaching()
            {
                base.OnDetaching();
                AssociatedObject.RemoveHandler(RoutedEvent, _handler);
            }
        }
    }
    

    Suppose there is CustomButton control that raises custom routed event ConditionalClick. It can be handled with ProcessConditionalClickCommand command in ViewModel using EventToCommandBehavior:

        <StackPanel>
            <b:Interaction.Behaviors>
                <behaviors:EventToCommandBehavior RoutedEvent="controls:CustomButton.ConditionalClick" WithCommand="{Binding ProcessConditionalClickCommand}">
                </behaviors:EventToCommandBehavior>
            </b:Interaction.Behaviors>
            <controls:CustomButton
                Content="Click to trigger a custom routed event"
                Background="LightGray">
            </controls:CustomButton>
        </StackPanel>