18

I want to execute a command when the user selects a row in a DataGrid.

I see it is possible to wrap cell contents in buttons (although I don't want the button style) - but I don't want to do it at the cell-level.

I also see it's possible to use behaviours to link a command to an event. But preferably I should not have to resort to behaviors for such a common task.

Is it possible to do this via plain old command databinding?

So: 1) user clicks DataGrid row 2) command on view model is fired.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Bent Rasmussen
  • 5,538
  • 9
  • 44
  • 63

2 Answers2

50

You should use "Interactivity" assembly and SelectionChanged event.

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding People}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="ID" Binding="{Binding ID}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
    </DataGrid.Columns>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding MyCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

Where "i" is namespace:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

Also you can write binding to SelectedItem property of the DataGrid and in the set accessor you can invoke your command, but the first solution that i presented you above is better.

If you want to invoke command from main view model and pass SelectedItem from DataGrid you can use CommadParameter:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction Command="{Binding MyCommand}" 
        CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

When items has own command you can use following code:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction Command="{Binding Path=SelectedItem.MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

Or if elements has own view model that is assigned to it's DataContext, you can use following code:

 <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <i:InvokeCommandAction Command="{Binding Path=SelectedItem.DataContext.MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
    </i:EventTrigger>
</i:Interaction.Triggers>
kmatyaszek
  • 19,016
  • 9
  • 60
  • 65
  • Marking as answer so far because it looks good and is reasonably simple. Thanks! I've also tried SelectedItem but had some issues due to virtualization. In general it appears the DataGrid has some bugs. – Bent Rasmussen Mar 10 '13 at 21:41
  • Okay, just tried and this then fires on the top-level view model, not the view model for each element in the DataGrid. I don't suppose I can make it fire directly on the relevant view model? Otherwise I guess I'll use SelectedItem as well. – Bent Rasmussen Mar 10 '13 at 22:12
  • 1
    @BentRasmussen check my answer again, I added more examples. – kmatyaszek Mar 10 '13 at 22:35
  • Triple-thumbs-up! You saved me a load of trouble! I had a very ugly back before. Thanks a million! – Bent Rasmussen Mar 11 '13 at 18:09
  • @kmatyaszek What would you say it is an advantage of executing a command over Binding SelectedItem to a viewmodel property SelectedObject? – Stefan Vasiljevic Feb 17 '16 at 14:35
  • That namespace reference doesn't appear to be working form me. "The type Interaction was not found", and/or "The name “Interaction” does not exist in the namespace" – Jonathan Tuzman May 08 '20 at 15:23
  • 1
    @JonathanTuzman please look at this question: https://stackoverflow.com/questions/8360209/how-to-add-system-windows-interactivity-to-project – kmatyaszek May 08 '20 at 15:44
2

Be advised that @kmatyaszek's answer is outdated in .NET 5.0 and above, we should use Microsoft.Xaml.Behaviors instead of Microsoft.Expression.Interactions.

So the i in the namespace should be:

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

See here for details.

Nowayout
  • 127
  • 9