Tag Archives: wpf

MVVM Light Toolkit’s EventToCommand Behaviour

The EventToCommand behaviour in the MVVM Light Toolkit is very cool in its ability to map events on GUI objects to commands in your ViewModel. However, you need to massage that a bit if you want to use it with some events, an example of which I will go through in this post.(Download-code link is at the end of the post.)

I plan to describe the process I went through, over the course of a weekend in downtime, working with the Telerik WPF RadGrid control and mapping the RowEditEnded event of the RadGrid to the UpdateAddressCommand on my ViewModel. Taking a step back, the goal was to implement edit operations on a grid, such that when the particular row being edited lost focus, an update of the values in that row would be made to the database.

Code Behind

First, the simplest and fastest way to implement the update was by using the code-behind class of the Window. So, the Xaml for that was as follows (for brevity, I’ll only display the Xaml for the whole file in this first example):

<Window x:Class="PassArgsNicelyMvvm.AddressWindowUsingCodeBehind"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" mc:Ignorable="d"                
        Title="AddressWindow" Height="200" Width="600"
        DataContext="{Binding AddressViewCodeBehind, Source={StaticResource Locator}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <telerik:RadGridView AutoGenerateColumns="False" ColumnWidth="*" GridLinesVisibility="None" telerik:StyleManager.Theme="Transparent"
                            IsReadOnly="False" ItemsSource="{Binding Path=Addresses}" RowEditEnded="RadGridView_RowEditEnded">
            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Number}" UniqueName="Number" Header="Nr" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Street}" UniqueName="Street" Header="Street" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Suburb}" UniqueName="Suburb" Header="Suburb" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PostCode}" UniqueName="PostCode" Header="Postcode" />
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>
    </Grid>
</Window>

And in the code-behind, the event handler was:

private void RadGridView_RowEditEnded(object sender, 
    Telerik.Windows.Controls.GridViewRowEditEndedEventArgs e)
{
    ((AddressViewModelMvvmBreach) DataContext).UpdateAddress(e.NewData as Address);
}

The problem with that approach is that you need to cast the DataContext of the Window to the ViewModel’s type (AddressViewModelMvvmBreach). Whilst you get the advantage of access to the data in the GridViewRowEditEndedEventArgs object, you stray a little from the MVVM pattern by casting the DataContext and directly calling the UpdateAddress method on the ViewModel. It gets the job done, but your architect may be somewhat displeased with your work.

EventToCommand – First Attempt

Realising that I wanted to conform as much as possible to the MVVM pattern and leverage the binding infrastructure which WPF gives us, I started looking at the EventToCommand feature in the MVVM Light Toolkit. My first attempt was as follows:

        <telerik:RadGridView AutoGenerateColumns="False" ColumnWidth="*" GridLinesVisibility="None" telerik:StyleManager.Theme="Transparent"
                            IsReadOnly="False" ItemsSource="{Binding Path=Addresses}">

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="RowEditEnded">
                    <EvtToCommand:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, 
                                                  Path=DataContext.UpdateAddressCommand}" PassEventArgsToCommand="False"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Number}" UniqueName="Number" Header="Nr" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Street}" UniqueName="Street" Header="Street" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Suburb}" UniqueName="Suburb" Header="Suburb" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PostCode}" UniqueName="PostCode" Header="Postcode" />
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>

Nothing was passed to the ViewModel. I needed to figure out how to pass the details of the object being edited to the UpdateAddressCommand of the ViewModel.

Bind on SelectedCellsChanged Event

So, I decided to come at it from a different direction and attempt to map the RadGridView’s SelectedCellsChanged event.

        <telerik:RadGridView AutoGenerateColumns="False" ColumnWidth="*" GridLinesVisibility="None" telerik:StyleManager.Theme="Transparent"
                            IsReadOnly="False" ItemsSource="{Binding Path=Addresses}">

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectedCellsChanged">
                    <EvtToCommand:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, 
                                                 Path=DataContext.UpdateAddressCommand}" PassEventArgsToCommand="False"
                                                 CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadGridView}, Path=SelectedItem}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>

            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Number}" UniqueName="Number" Header="Nr" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Street}" UniqueName="Street" Header="Street" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Suburb}" UniqueName="Suburb" Header="Suburb" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PostCode}" UniqueName="PostCode" Header="Postcode" />
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>

At first, I thought I had cracked it. But upon closer examination of the data, I observed that the data being passed to the ViewModel was that of the row that I was clicking towards, rather than that of the cell that I had just changed/edited.

PassEventArgsToCommand

I then discovered that the EventToCommand behaviour has an attribute PassEventArgsToCommand, enabling you to pass the EventArgs of the event which you are mapping to your ViewModel:

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="RowEditEnded">
                    <EvtToCommand:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, 
                                                  Path=DataContext.UpdateAddressCommand}" PassEventArgsToCommand="True" />
                </i:EventTrigger>
            </i:Interaction.Triggers>

Again, for an hour or so I was completely pleased with myself. Until I realised that for this to work, you needed a reference to a specific View technology in your ViewModel. That is, the UpdateAddress method now looked like this, with a Telerik technology in the signature:

        public void UpdateAddress(GridViewRowEditEndedEventArgs address)
        {
            Address addressToUpdate = address.NewData as Address;
            // call service to update
            Trace.WriteLine(addressToUpdate);
        }

This is probably a bit of a hot topic and many people would see it as acceptable. But others would claim that the ViewModel should be technology-agnostic.

Sub-class the EventToCommand Behaviour

After looking long and hard at the situation, I decided to take a peak inside the EventToCommand behaviour. One of the great advantages of open source projects is that you can see exactly what they are doing. And one thing I found was a virtual method called Invoke, which took an object as a parameter. And debugging through, I noted that in this case, the object was the GridViewRowEditEndedEventArgs of the RowEditEnded event. I could see a way forward. All had to do was create a subclass of the EventToCommand behaviour, override the Invoke method and cast the parameter to the GridViewRowEditEndedEventArgs class. That would enable me to extract the data for the row which I had just edited and then pass that data to my ViewModel.

    public class RowEditEndedToCommand : EventToCommand
    {
        protected override void Invoke(object parameter)
        {
            GridViewRowEditEndedEventArgs args = parameter as GridViewRowEditEndedEventArgs;

            if (Command != null)
            {
                Command.Execute(args.NewData);
            }
        }     
    }

Used in Xaml:

<telerik:RadGridView AutoGenerateColumns="False" ColumnWidth="*" GridLinesVisibility="None" telerik:StyleManager.Theme="Transparent"
                            IsReadOnly="False" ItemsSource="{Binding Path=Addresses}">

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="RowEditEnded">
                    <etocmd:RowEditEndedToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, 
                                                  Path=DataContext.UpdateAddressCommand}" PassEventArgsToCommand="False"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Number}" UniqueName="Number" Header="Nr" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Street}" UniqueName="Street" Header="Street" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding Suburb}" UniqueName="Suburb" Header="Suburb" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PostCode}" UniqueName="PostCode" Header="Postcode" />
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>

I am not a stickler for patterns. Whilst I enjoy maintaining code which has been written to a pattern, the odd departure from the pattern is fine where commercial reality dictates it. But here is a case where you can have your cake and eat it. A simple usage of inheritance enabled us to keep a specific View technology out of our ViewModel, whilst not having to rely on the code-behind of the view. In short, the integrity of the MVVM pattern was maintained in a simple solution.

Get the code (Telerik DLLs removed):

DependencyPropertyDescriptor

One of the cool features of dependency properties is that there is a nice, cheap and easy way to incorporate notifications using them. (Note, however, that a more sophisticated notification system may be required by your needs and would otherwise have to be implemented in a different way).

There’s this class called DependencyPropertyDescriptor. MSDN says, “Provides an extension of PropertyDescriptor that accounts for the additional property characteristics of a dependency property.” So, it inherits from the abstract class, PropertyDescriptor, which “Provides an abstraction of a property on a class.”

You can create a DependencyPropertyDescriptor object using its class’ static method “FromProperty”. Then, all you need to do is call the AddValueChanged method, passing in a control and a callback method, and then whenever that property’s value changes, the callback method will be invoked. The following code is a portion of the code-behind for the demo which can be downloaded by clicking here:

            DependencyPropertyDescriptor dpd =
                DependencyPropertyDescriptor.FromProperty
                    (FrameworkElement.HeightProperty,
                    typeof(FrameworkElement)
                    );

            //  Now add btnHi to the descriptor. Note that btnBye is not added.
            dpd.AddValueChanged(btnHi, HeightValueChanged);

The callback method is simply:

        private void HeightValueChanged(Object sender, EventArgs e)
        {
            MessageBox.Show("height is now " + ((Button)sender).Height.ToString());
        }

And the rest you can see in the download code.