Álvaro García
Álvaro García

Reputation: 19356

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.

Upvotes: 1

Views: 791

Answers (1)

zukimo
zukimo

Reputation: 106

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>

Upvotes: 2

Related Questions