WPF MVVMLight Toolkit & Capturing Control Events

If you have worked with MVVMLight toolkit framework, then you must be familiar with the RelayCommand pattern for processing control commands. For example, if you have to handle the button click event using MVVMLight framework i.e. to not have the code-behind button click event handler but instead call some method in your ModelView class, then you can do it easily using the RelayCommand pattern. Below is a sample example :

Say you have defined a button in your XAML file :

<Button Name="BtnRun"
Margin="3" FontSize="18" FontWeight="Bold"
Foreground="Black" Background="LightGray"
BorderThickness="2" 
HorizontalAlignment="Center" 
VerticalAlignment="Center"
Command="{Binding ExecuteCommand}">Click Me</Button>

Instead of handling the OnClick event, we have provided binding for the “Command” property of the button. Now in our view-model class, we can define the “ExecuteCommand” relay command. Code :

public class MainViewModel : ViewModelBase
{
    public RelayCommand ExecuteBatchCommand { get; private set; }
    public MainViewModel()
    {
        ExecuteBatchCommand = new RelayCommand(ExecuteButton);
    }

    ////public override void Cleanup()
    ////{
    ////    // Clean up if needed

    ////    base.Cleanup();
    ////}

    public void ExecuteButton()
    {
  .....
    }
}

As we can see, handling the button command is as simple as defining a RelayCommand property and initializing it with a System.Action delegate instance.

The situation gets complicated when the some controls, do not offer any Command property for e.g. in case of DataGrid control, it offers various events like OnAutoGeneratingColumn & OnLoadingRow. The problem with MVVMLight framework approach is that there is no easy way to handle such events.

MVVMLight framework does offer the “EventTrigger” approach e.g.

<i:Interaction.Triggers>
    <i:EventTrigger>
        <cmd:EventToCommand Command="{Binding MouseMoveCommand, Source={StaticResource MyVM}}" PassEventArgsToCommand="True"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

But the problem with event trigger based event handling is that the control has been drawn & is ready for user interaction. In the case of DataGrid’s OnAutoGeneratingColumns & OnLoadingRow, the controls are not yet rendered but instead only initialized and these events offer users a way to make the last minute changes before the controls are rendered. So how do we handle these events using MVVMLight framework without breaking the “No Code Behind” oath.

Unfortunately we will have to handle the event in code-behind but the good things is from the event handler we can invoke the “RelayCommand” back in our view-model class. Lets see some code:

DataGrid with OnAutoGeneratingColumn event set:

<DataGrid ItemsSource="{Binding TimeTrackerData}" 
HorizontalAlignment="Stretch"
AutoGenerateColumns="True" 
BorderThickness="1" Height="500"
ScrollViewer.CanContentScroll="True" 
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
Name="SubmittedGrid" 
AutoGeneratingColumn="SubmittedJobGrid_OnAutoGeneratingColumn"
CanUserAddRows="False">

The corresponding code-behind event handler :

private void SubmittedGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs eventArgs)
{
  // we will add the implementation later   
}

As we can see, the event handler takes two parameters i.e. sender(DataGrid itself) & the DataGridAutoGeneratingColumnEventArgs. Lets create a new class called DataGridCommandArgs containing these two properties :

public class DataGridCommandArgs
{
    public DataGrid Grid { get; set; }
    public DataGridAutoGeneratingColumnEventArgs ColumnEventArgs { get; set; }

    public DataGridCommandArgs(DataGrid grid, DataGridAutoGeneratingColumnEventArgs eventArgs)
    {
        this.Grid = grid;
        this.ColumnEventArgs = eventArgs;
    }
}

Now its time to define the “RelayCommand   in our view-model class.

public class MainViewModel : ViewModelBase
{
    public RelayCommand<DataGridCommandArgs> TestCommand { get; private set; }

    /// <summary>
    /// Initializes a new instance of the MainViewModel class.
    /// </summary>
    public MainViewModel()
    {
        TestCommand = new RelayCommand<DataGridCommandArgs>(TestRowDataBound);
    }

    ////public override void Cleanup()
    ////{
    ////    // Clean up if needed

    ////    base.Cleanup();
    ////}

    public void TestRowDataBound(DataGridCommandArgs e)
    {
        // Add custom logic here..
    }
}

Now that we have our ViewModel class set, its time to fill the OnAutoGeneratingColumn event handler implementation :

private void SubmittedJobGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs eventArgs)
{
    DataGrid grid = sender as DataGrid;
    DataGridCommandArgs commandArgs = new DataGridCommandArgs(grid, eventArgs);
    var vm = ((MainViewModel)this.DataContext);
    if (vm.TestCommand.CanExecute(commandArgs))
        vm.TestCommand.Execute(commandArgs);
}

The above code assumes that the ViewModel class is set as the main DataContext of the app. Rest of the code is self-explanatory. Hope you have enjoyed reading this blog post.

Contact Me

rk.pawan@gmail.com | +1-737-202-7676 | LinkedIn | Facebook | Github |

No Comments

Add a Comment

As it will appear on the website

Not displayed

Your website