EventToCommand

  • 9 minutes to read

The EventToCommand class is a Behavior that allows you to bind an event to a command. This way, the bound command is invoked like an event handler when the event is raised.

This topic describes how to use the EventToCommand behavior.

Getting Started With EventToCommand

Assume that there is a ListBox control that displays data. When an end user clicks an item in the ListBox, it is necessary to show an edit form.

To accomplish this task, you can use the EventToCommand behavior. Place the EventToCommand into the Interaction.Behaviors collection for the ListBox control and customize it as shown below.

<UserControl x:Class="Example.View.MainView" ...
    xmlns:ViewModel="clr-namespace:Example.ViewModel"
    xmlns:Common="clr-namespace:Example.Common"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
    DataContext="{dxmvvm:ViewModelSource Type=ViewModel:MainViewModel}">
    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox ItemsSource="{Binding Persons}">
            <dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="MouseDoubleClick" Command="{Binding EditCommand}">
                    <dxmvvm:EventToCommand.EventArgsConverter>
                        <Common:ListBoxEventArgsConverter/>
                    </dxmvvm:EventToCommand.EventArgsConverter>
                </dxmvvm:EventToCommand>
            </dxmvvm:Interaction.Behaviors>
            <ListBox.ItemTemplate>
                ...
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

This code defines an EventToCommand that processes the MouseDoubleClick event for the ListBox. When the event is raised, the EventToCommand invokes the bound EditCommand. This command requires a parameter: a Person object to be edited.

The code below shows the EditCommand implementation code both in a POCO ViewModel and a common ViewModel.

//POCO ViewModel
[POCOViewModel]
public class MainViewModel {
    public void Edit(Person person) {
        ...
    }
    public bool CanEdit(Person person) {
        return person != null;
    }
}
//Common ViewModel
public class MainViewModel {
    public ICommand<Person> EditCommand { get; private set; }
    public MainViewModel() {
        EditCommand = new DelegateCommand<Person>(Edit, CanEdit);
    }
    public void Edit(Person person) {
        ... 
    }
    public bool CanEdit(Person person) {
        return person != null;
    }
}

In this case, it is necessary to take the clicked ListBoxItem and obtain its DataContext - this is what the EventToCommand should pass to the EditCommand as a parameter. This operation is performed by the custom ListBoxEventArgsConverter. Its code is shown below.

using DevExpress.Mvvm.UI;
using System.Linq;
public class ListBoxEventArgsConverter : EventArgsConverterBase<MouseEventArgs> {
    protected override object Convert(object sender, MouseEventArgs args) {
        ListBox parentElement = (ListBox)sender;
        DependencyObject clickedElement = (DependencyObject)args.OriginalSource;
        ListBoxItem clickedListBoxItem = 
            LayoutTreeHelper.GetVisualParents(child: clickedElement, stopNode: parentElement)
            .OfType<ListBoxItem>()
            .FirstOrDefault();
        if(clickedListBoxItem != null)
            return (Person)clickedListBoxItem.DataContext;
        return null;
    }
}

The ListBoxEventArgsConverter class implements the IEventArgsConverter interface. This interface provides the Convert method, which is used by the EventToCommand for conversion event arguments.

In this scenario, the EventToCommand passes a MouseEventArgs object to the ListBoxEventArgsConverter. The converter finds the clicked ListBoxItem using the LayoutTreeHelper class and returns its DataContext, which contains an underlying Person object. The resulting Person object is then passed to the bound EditCommand.

Specifying an Event

The EventToCommand behavior allows you to specify an event using any of the following properties:

EventName is useful when a source object provides an event.

<dxe:TextEdit>
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand Command="{Binding LoadedCommand}" EventName="Loaded" />
    </dxmvvm:Interaction.Behaviors>
</dxe:TextEdit>

Unlike EventName, the Event property is of the RoutedEvent type and can be used to specify attached events. For example:

<dxe:TextEdit>
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand Command="{Binding LoadedCommand}" Event="FrameworkElement.Loaded" />
        <dxmvvm:EventToCommand Command="{Binding TextChangedCommand}" Event="TextBoxBase.TextChanged" />
    </dxmvvm:Interaction.Behaviors>
</dxe:TextEdit>

Specifying a Source Object

The source object is an object that provides the event for processing by the EventToCommand. By default, the EventToCommand processes the defined event for the associated control. This scenario is shown below.

<UserControl ...>
    ...
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding InitializeCommand}"/>
    </dxmvvm:Interaction.Behaviors>
    ...
</UserControl>

If it's necessary, you can manually specify the source object for the EventToCommand. To do this, bind the EventTriggerBase<T>.SourceObject property, or specify the object's name using the EventTriggerBase<T>.SourceName property.

<UserControl ...>
    ...
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand SourceName="list" EventName="MouseDoubleClick" Command="{Binding InitializeCommand}"/>
        <dxmvvm:EventToCommand SourceObject="{Binding ElementName=list}" EventName="MouseDoubleClick" Command="{Binding InitializeCommand}"/>
    </dxmvvm:Interaction.Behaviors>
    ...
        <ListBox x:Name="list" ... />
    ...
</UserControl>

Passing a Parameter to the Bound Command

You can provide a parameter to the bound command using the EventToCommandBase.CommandParameter property.

<ListBox x:Name="list" ...>
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand EventName="MouseDoubleClick" Command="{Binding EditCommand}" CommandParameter="{Binding ElementName=list, Path=SelectedItem}"/>
    </dxmvvm:Interaction.Behaviors>
</ListBox>

Alternatively, you can pass the event's arguments to the command as a parameter by setting the EventToCommand.PassEventArgsToCommand property to true.

<ListBox x:Name="list" ...>
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand EventName="MouseDoubleClick" Command="{Binding EditCommand}" PassEventArgsToCommand="True"/>
    </dxmvvm:Interaction.Behaviors>
</ListBox>

If you have a clean MVVM architecture, you may not want to pass event arguments to View Models. In this case, you can convert the event arguments to an object suitable for the command. To do this, specify a converter using the EventToCommand.EventArgsConverter property.

<ListBox x:Name="list" ...>
    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand EventName="MouseDoubleClick" Command="{Binding EditCommand}">
            <dxmvvm:EventToCommand.EventArgsConverter>
                <Common:CustomEventArgsConverter/>
            </dxmvvm:EventToCommand.EventArgsConverter>
        </dxmvvm:EventToCommand>
    </dxmvvm:Interaction.Behaviors>
</ListBox>

The defined EventArgsConverter should implement the IEventArgsConverter interface. You can also derive the converter from the EventArgsConverterBase<TArgs> class, which already implements the IEventArgsConverter interface.

public interface IEventArgsConverter {
    object Convert(object sender, object args);
}
public abstract class EventArgsConverterBase<TArgs> : IEventArgsConverter {
    protected EventArgsConverterBase();
    protected abstract object Convert(object sender, TArgs args);
}

When implementing the converter, you can use the LayoutTreeHelper class that provides useful functions for searching nodes in the visual tree. For instance:

using DevExpress.Mvvm.UI;
using System.Linq;
public class ListBoxEventArgsConverter : EventArgsConverterBase<MouseEventArgs> {
    protected override object Convert(object sender, MouseEventArgs args) {
        ListBox parentElement = (ListBox)sender;
        DependencyObject clickedElement = (DependencyObject)args.OriginalSource;
        ListBoxItem clickedListBoxItem = 
            LayoutTreeHelper.GetVisualParents(child: clickedElement, stopNode: parentElement)
            .OfType<ListBoxItem>()
            .FirstOrDefault();
        return clickedListBoxItem != null ? clickedListBoxItem.DataContext : null;
    }
}

Specifying Modifier Keys as an Additional Condition of the Command Execution

The EventToCommand behavior allows you to invoke a command only when modifier keys are pressed. Use the EventToCommand.ModifierKeys property to specify modifier keys.

<UserControl x:Class="Example.View.MainView" ...
    xmlns:ViewModel="clr-namespace:Example.ViewModel"
    xmlns:Common="clr-namespace:Example.Common"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
    DataContext="{dxmvvm:ViewModelSource Type=ViewModel:MainViewModel}">
    ...
        <ListBox ItemsSource="{Binding Persons}">
            <dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="MouseLeftButtonUp" Command="{Binding EditCommand}" ModifierKeys="Ctrl+Alt">
                    <dxmvvm:EventToCommand.EventArgsConverter>
                        <Common:ListBoxEventArgsConverter/>
                    </dxmvvm:EventToCommand.EventArgsConverter>
                </dxmvvm:EventToCommand>
            </dxmvvm:Interaction.Behaviors>
            ...
        </ListBox>
    ...
</UserControl>

In the example above, the bound EditCommand is invoked when an end user clicks a ListBoxItem while pressing the Ctrl and Alt keys.

Marking Routed Events as Handled

Set the EventToCommandBase.MarkRoutedEventsAsHandled property to True to mark routed events as handled when the bound command is executed.

<dxmvvm:EventToCommand MarkRoutedEventsAsHandled="True" .../>

In this case, the EventToCommand sets the e.Handled parameter of corresponding event arguments to True right after the bound command is executed.

Using the Dispatcher to Invoke the bound Command

The EventToCommand class provides the EventToCommandBase.DispatcherPriority property. You can set this property to use the Dispatcher to invoke the bound command.

Disabling a Control When a Command Cannot be Executed

The EventToCommand class provides the EventToCommand.AllowChangingEventOwnerIsEnabled property, which is False by default. If you set this property to True, the EventToCommand disables (sets the IsEnabled property to False) the associated control when the bound command cannot be executed (when the ICommand.CanExecute method returns False).

Processing Events of a Disabled Control

The EventToCommand class provides the EventToCommandBase.ProcessEventsFromDisabledEventOwner property, which is True by default. This means that the EventToCommand processes events for the associated control even if the associated control is disabled. If you need to prevent this behavior, set the ProcessEventsFromDisabledEventOwner property to False.

Example

NOTE

A complete sample project is available at https://github.com/DevExpress-Examples/how-to-use-eventtocommand-t142075.

<UserControl x:Class="Example.View.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModel="clr-namespace:Example.ViewModel"
    xmlns:Common="clr-namespace:Example.Common"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignHeight="500" d:DesignWidth="600"
    DataContext="{dxmvvm:ViewModelSource Type=ViewModel:MainViewModel}">

    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding InitializeCommand}"/>
        <dx:DXMessageBoxService/>
    </dxmvvm:Interaction.Behaviors>

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Press Ctrl and click an item or select an item and press the Ctrl+Enter key combination."/>
        <ListBox x:Name="list" ItemsSource="{Binding Persons}" Grid.Row="1">
            <dxmvvm:Interaction.Behaviors>
                <dxmvvm:EventToCommand EventName="MouseLeftButtonUp" Command="{Binding EditCommand}" ModifierKeys="Ctrl">
                    <dxmvvm:EventToCommand.EventArgsConverter>
                        <Common:ListBoxEventArgsConverter/>
                    </dxmvvm:EventToCommand.EventArgsConverter>
                </dxmvvm:EventToCommand>
                <dxmvvm:KeyToCommand Command="{Binding EditCommand}" KeyGesture="Ctrl+Enter" CommandParameter="{Binding ElementName=list, Path=SelectedItem}"/>
            </dxmvvm:Interaction.Behaviors>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock>
                        <Run Text="{Binding FirstName}"/> <Run Text="{Binding LastName}"/>
                    </TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>