DevExpress MVVM Framework. EventToCommand.

WPF Team Blog
02 September 2014

In a previous post, we reviewed how the Behavior mechanism operates and touched on one of the most useful behaviors, EventToCommand. Today, we examine EventToCommand in detail and run through its features.

We’ll start from scratch with a review of why and how to use EventToCommand. This post includes the following sections:

  • Getting started with EventToCommand
  • Setting a source object
  • Passing a parameter to the bound command
  • Specifying modifier keys as additional conditions for command execution (this feature will be available in the next minor release, 14.1.7)
  • Marking routed events as handled
  • Invoking the bound command with Dispatcher
  • Disabling a control when a command can’t be executed
  • Processing events for a disabled control

All the features described in this post are available in both the free and non-free versions of the DevExpress MVVM framework (included in the DevExpress WPF component suite).


Getting started with EventToCommand

Let’s start with a ListBox control that displays data. When an end-user clicks a ListBox item, they need to be shown an edit form for the record.

You can perform this task with the EventToCommand behavior. Place an EventToCommand in the Interaction.Behaviors collection for the ListBox control and customize it as 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 command, EditCommand. This command accepts a parameter, in this case the Person object being edited.

The code below is our EditCommand implementation. This Command is automatically generated from the Edit and CanEdit methods by the DevExpress POCO mechanism.

[POCOViewModel]
public class MainViewModel {
    public void Edit(Person person) {
        ...
    }
    public bool CanEdit(Person person) {
        return person != null;
    }
}

In this case, our command will need to access the DataContext of the clicked ListBoxItem. Our EventToCommand passes a parameter to the EditCommand via a custom ListBoxEventArgsConverter which implements the IEventArgsConverter interface. This IEventArgsConverter interface defines a Convert method, used by the EventToCommand to convert event arguments into the command parameter.

public class ListBoxEventArgsConverter : EventArgsConverterBase<MouseEventArgs> {
    protected override object Convert(object sender, MouseEventArgs args) {
        ListBox parentElement = (ListBox)sender;
        DependencyObject clickedElement = (DependencyObject)args.OriginalSource;
        ListBoxItem clickedListBoxItem = VisualTreeHelperEx.FindParent<ListBoxItem>(
            child: clickedElement, 
            stopSearchNode: parentElement);
        if(clickedListBoxItem != null)
            return (Person)clickedListBoxItem.DataContext;
        return null;
    }
}

In this scenario, the EventToCommand passes a MouseEventArgs to the ListBoxEventArgsConverter. The converter finds the clicked ListBoxItem using the VisualTreeHelperEx class (this class is available in the next minor release, 14.1.7) and returns its DataContext, the Person object to be edited. The Person is passed to the bound EditCommand.

 

Setting a source object

The source object furnishes the event used by the EventToCommand. By default, EventToCommand looks for an event on an associated control matching the EventToCommand.EventName property. This scenario is shown below.

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

Alternatively, you can manually specify the EventToCommand source object. Use a binding on the EventToCommand.SourceObject property, or specify a source object by name using the EventToCommand.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 for the bound command with the EventToCommand.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 directly pass event arguments to the command 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> 

In a clean MVVM architecture, you may want to avoid passing event arguments to View Models. In which case, you can first convert event arguments to an appropriate object for the command. Simply use a converter in 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> 

EventArgsConverter should implement the IEventArgsConverter interface. You can also derive a converter from the EventArgsConverterBase<TArgs> class, which already implements the IEventArgsConverter interface (in the next minor release, 14.1.7, the IEventArgsConverter interface will be updated to include a sender type parameter).

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 VisualTreeHelperEx class (available in the next minor release, 14.1.7) which provides useful functions for searching nodes in the visual tree. For instance:

public class ListBoxEventArgsConverter : EventArgsConverterBase<MouseEventArgs> {
    protected override object Convert(object sender, MouseEventArgs args) {
        ListBox parentElement = (ListBox)sender;
        DependencyObject clickedElement = (DependencyObject)args.OriginalSource;
        ListBoxItem clickedListBoxItem = VisualTreeHelperEx.FindParent<ListBoxItem>(
            child: clickedElement, 
            stopSearchNode: parentElement);
        return clickedListBoxItem != null ? clickedListBoxItem.DataContext : null;
    }
}

 

Specifying modifier keys as additional conditions for command execution

This feature will be available in the next minor release, 14.1.7.

There are times when it’s necessary to invoke a command only when specific modifier keys are pressed. These additional conditions for command execution can be specified in the EventToCommand.ModifierKeys property.

<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 only invoked when an end-user clicks a ListBoxItem with the Ctrl and Alt keys pressed.

 

Marking routed events as handled

When handling a routed event with EventToCommand, you may want to mark the routed event as handled once the bound command executes. To enable this behavior, set the EventToCommand.MarkRoutedEventsAsHandled property to True.

<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.

 

Invoking the bound command with Dispatcher

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

 

Disabling a control when a command can’t be executed

The EventToCommand class has an AllowChangingEventOwnerIsEnabled property, 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 for a disabled control

The EventToCommand class provides the ProcessEventsFromDisabledEventOwner property, True by default. This means that the EventToCommand handles 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.


OTHER RELATED ARTICLES:

  1. Getting Started with DevExpress MVVM Framework. Commands and View Models.
  2. DevExpress MVVM Framework. Introduction to Services, DXMessageBoxService and DialogService.
  3. DevExpress MVVM Framework. Interaction of ViewModels. IDocumentManagerService.
  4. DevExpress MVVM Framework. Introduction to POCO ViewModels.
  5. DevExpress MVVM Framework. Interaction of ViewModels. Messenger.
  6. DevExpress MVVM Framework. Using Scaffolding Wizards for building Views.
  7. DevExpress MVVM Framework. Data validation. Implementing IDataErrorInfo.
  8. DevExpress MVVM Framework. Using DataAnnotation attributes and DevExpress Fluent API.
  9. DevExpress MVVM Framework. Behaviors.
  10. DevExpress MVVM Framework. TaskbarButtonService, ApplicationJumpListService and NotificationService.
  11. DevExpress MVVM Framework. Asynchronous Commands.
  12. DevExpress MVVM Framework. Converters.
  13. THIS POST: DevExpress MVVM Framework. EventToCommand.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.