37

I know how to use the MouseDoubleClick event with my DataGrid to grab the selectedvalue, but how would one go about using command bindings instead? That way my ViewModel can handle the logic.

So far I have the following:

<DataGrid Name="TestGrid" Grid.Row="2" Grid.ColumnSpan="2" AutoGenerateColumns="True" MouseDoubleClick="TestGrid_MouseDoubleClick"
          ItemsSource="{Binding Registrations}" SelectedValue="{Binding CurrentRegistration}" IsReadOnly="True" AlternationCount="2" GridLinesVisibility="None">

I want to get rid of MouseDoubleClick and replace it appropriately.

myermian
  • 31,823
  • 24
  • 123
  • 215
  • Have you checked this [post](http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/632ea875-a5b8-4d47-85b3-b30f28e0b827), provides entire solution... – Aaron McIver Oct 06 '10 at 20:47

4 Answers4

98

No need for attached behaviors or custom DataGrid subclasses here.

In your DataGrid, bind ItemsSource to an ICollectionView. The trick here is to set IsSynchronizedWithCurrentItem="True" which means the selected row will be the current item.

The second part of the trick is to bind CommandParameter to the current item with the forward slash syntax.

When a row is double clicked, the command will be executed with the clicked row as argument.

<DataGrid
    ItemsSource="{Binding CollectionView}"
    IsSynchronizedWithCurrentItem="True">
    <DataGrid.InputBindings>
        <MouseBinding
            MouseAction="LeftDoubleClick"
            Command="{Binding DoubleClickCommand}"
            CommandParameter="{Binding CollectionView/}"/>
    </DataGrid.InputBindings>
</DataGrid>

This is how a (simplified) version of the view model would look:

class MyViewModel
{
    public ICollectionView CollectionView { get; set; }

    public ICommand DoubleClickCommand { get; set; }
}
metal
  • 6,202
  • 1
  • 34
  • 49
Mizipzor
  • 51,151
  • 22
  • 97
  • 138
  • 3
    is the `/` in your `{Binding CollectionView/}` on purpose? – Maslow Sep 23 '15 at 17:41
  • 11
    Yes, that makes it bind to the current item. Which when used with `IsSynchronizedWithCurrentItem` means the selected item. [Here's](http://www.thejoyofcode.com/binding_to_the_current_item_in_wpf.aspx) a blog post. – Mizipzor Oct 01 '15 at 09:48
  • 5
    Very neat solution. Also didn't know about the forward slash binding. Sometimes I'm really astonished where people know this kind of things from. Thank you! – Julian Sievers Nov 14 '15 at 15:51
  • 4
    This is a good solution. But there is a drawback here. If one double clicks DataGrids' header, the command will be executed anyway. In some scenarios this might be undesirable thing for the developer. How can it be resolved that? – T.Y. Kucuk Mar 07 '16 at 19:33
  • As I understand it, you can use the "CanExecute" part of the ICommand implementation to determine whether the parameter passed is a valid one for running the command on – simonalexander2005 Jun 03 '16 at 13:26
  • 1
    When I use the forward slash on the parameter, it always passes the item in the first row. I do have `IsSynchronizedWithCurrentItem="True"`. The grid is bound to an `ObservableCollection`. Is there anything I'm missing re. the virtualisation? – ProfK Nov 29 '16 at 09:13
  • I give +1 but I think is hack anyway. – LukaszTaraszka Oct 06 '17 at 14:43
  • 3
    Note, that if `IsReadOnly="False"` for your DataGrid, then the first click may be stolen the selection mechanism. Set `IsReadOnly="True"` to fix that up. – Dmitriy Nemykin Aug 29 '18 at 12:04
  • 1
    Personally I don't like the `Binding CollectionView/`. Got this to work if you give the `DataGrid` a name and in the command parameter of the mouse binding do the following: `CommandParameter="{Binding ElementName=SomeGridName, Path=SelectedItem}"` – Tyler B. Long May 16 '19 at 19:02
  • Why so many black magic in WPF/XAML? Where's the doc to describe what is the slash? – huang Apr 27 '21 at 02:23
34

Another solution is to add input bindings, and to bind the selectedItem to a property so you'll know which one was selected:

<DataGrid SelectedItem="{Binding SelectedItem}">
      <DataGrid.InputBindings>
          <MouseBinding Gesture="LeftDoubleClick" Command="{Binding SomeCommand}"/>
     </DataGrid.InputBindings>
</DataGrid>
Tamar Cohen
  • 1,272
  • 2
  • 16
  • 28
  • Nice and elegant. – MaYaN Oct 26 '17 at 11:18
  • 4
    Nice but there is an issue. Lets say you have a datagrid with multiple columns and the last column is not set to width=* and you double click to the far right in extra space after last column the selected item will not update and it will pass whatever the selected item was before. And if no selected item exists yet it will pass null. – JMIII Jul 26 '20 at 18:55
3

Use this library

Sample binding to datagrid event:

<DataGrid xmlns:command="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
    command:CommandBehavior.Event="MouseDoubleClick"
    command:CommandBehavior.Command="{Binding TestCommand}" />

But this code is better, because raises on row clicks only:

<DataGrid>
    <DataGrid.Resources>
        <Style TargetType="DataGridRow">
            <Setter Property="command:CommandBehavior.Event" Value="MouseDoubleClick"/>
            <Setter Property="command:CommandBehavior.Command" Value="{Binding DataContext.TestCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
        </Style>
    </DataGrid.Resources>
</DataGrid>
vortexwolf
  • 13,967
  • 2
  • 54
  • 72
  • I like the library, but it seems to contain more than what I need (for the moment). I'll look at the library and if I need more than the double click command then perhaps this will contain other commands I wouldn't mind having. – myermian Apr 16 '11 at 04:42
  • @myermian You can also take a look at MVVM Light, it has the similar class and other classes which help to use the MVVM pattern. For me it is easier to add reference to a library than to copy the code, but it is controversial question whether to use 3rd party libraries or to write your own. – vortexwolf Apr 16 '11 at 12:51
2

Or, you could create a derived class

public class CustomDataGrid : DataGrid
{
    public ICommand DoubleClickCommand
    {
        get { return (ICommand)GetValue(DoubleClickCommandProperty); }
        set { SetValue(DoubleClickCommandProperty, value); }
    }

    // Using a DependencyProperty as the backing store for DoubleClickCommand.  This    enables animation, styling, binding, etc...
    public static readonly DependencyProperty DoubleClickCommandProperty =
        DependencyProperty.Register("DoubleClickCommand", typeof(ICommand), typeof(CustomDataGrid), new UIPropertyMetadata());

    public CustomDataGrid()
        : base()
    {            
        this.PreviewMouseDoubleClick += new MouseButtonEventHandler(CustomDataGrid_PreviewMouseDoubleClick);
    }


    void CustomDataGrid_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        if (DoubleClickCommand != null)
        {
            DoubleClickCommand.Execute(null);
        }
    }


}

and in XAML simply bind to the newly created command

<CustomDataGrid DoubleClickCommand="{Binding DoubleClickCommand}">