none
MouseDouble click or singleClick on datagrid in MVVM how can I do that?

    Question

  • Hi guys

    I am sort of new to wpf.It should be something very basic to achieve but I am struggliing.

    I have a datagrid and when I user preferably single click or double click on a datagrid row should launch a new form.

    I cannot seem to do it. I cannot use 3rd party libraries etc.. I have looked over the net found a link on codeplex but cannot seem to make it work at all using mvvm and binding to a command  the link is this http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing

    Also I have noticed that the datagrid supports mouseDoubleClick. How to bind it to a command? I have read that I should use attached properties but cannot make a simple example work. Could help me with some simple code snippet?

     

    Thanks a lot for any input


    Thanks for your help
    Saturday, July 10, 2010 5:46 AM

Answers

  • Hi devBrix,

    you can create own dependency property DataGridDoubleClickProperty where you attach handler for DataGrid.MouseDoubleClick event

    public static class Commands
    {
      public static readonly DependencyProperty DataGridDoubleClickProperty =
        DependencyProperty.RegisterAttached("DataGridDoubleClickCommand", typeof ( ICommand ), typeof ( Commands ),
                          new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveDataGridDoubleClickEvent)));
    
      public static ICommand GetDataGridDoubleClickCommand(DependencyObject obj)
      {
        return (ICommand) obj.GetValue(DataGridDoubleClickProperty);
      }
    
      public static void SetDataGridDoubleClickCommand(DependencyObject obj, ICommand value)
      {
        obj.SetValue(DataGridDoubleClickProperty, value);
      }
    
      public static void AttachOrRemoveDataGridDoubleClickEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
      {
        DataGrid dataGrid = obj as DataGrid;
        if ( dataGrid != null )
        {
          ICommand cmd = (ICommand) args.NewValue;
    
          if ( args.OldValue == null && args.NewValue != null )
          {
            dataGrid.MouseDoubleClick += ExecuteDataGridDoubleClick;
          }
          else if ( args.OldValue != null && args.NewValue == null )
          {
            dataGrid.MouseDoubleClick -= ExecuteDataGridDoubleClick;
          }
        }
      }
    
      private static void ExecuteDataGridDoubleClick(object sender, MouseButtonEventArgs args)
      {
        DependencyObject obj = sender as DependencyObject;
        ICommand cmd = (ICommand) obj.GetValue(DataGridDoubleClickProperty);
        if ( cmd != null )
        {
          if ( cmd.CanExecute(obj) )
          {
            cmd.Execute(obj);
          }
        }
      }
    
    }
    

    In your View you use Binding to map this DependencyProperty to Command

    <Grid DataContext="{StaticResource viewModel}">
      <DataGrid AutoGenerateColumns="True" 
            ItemsSource="{Binding Data}" 
            SelectedItem="{Binding SelectedItem}" 
            clr:Commands.DataGridDoubleClickCommand="{Binding DataGridDoubleClick}"
        />
    </Grid>
    

    DataGridDoubleClick is ICommand property in your ViewModel class

    Hope this help

    Lukas

    • Marked as answer by devBrix Sunday, July 11, 2010 5:57 AM
    Saturday, July 10, 2010 11:00 AM

All replies

  • Hi devBrix,

    you can create own dependency property DataGridDoubleClickProperty where you attach handler for DataGrid.MouseDoubleClick event

    public static class Commands
    {
      public static readonly DependencyProperty DataGridDoubleClickProperty =
        DependencyProperty.RegisterAttached("DataGridDoubleClickCommand", typeof ( ICommand ), typeof ( Commands ),
                          new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveDataGridDoubleClickEvent)));
    
      public static ICommand GetDataGridDoubleClickCommand(DependencyObject obj)
      {
        return (ICommand) obj.GetValue(DataGridDoubleClickProperty);
      }
    
      public static void SetDataGridDoubleClickCommand(DependencyObject obj, ICommand value)
      {
        obj.SetValue(DataGridDoubleClickProperty, value);
      }
    
      public static void AttachOrRemoveDataGridDoubleClickEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
      {
        DataGrid dataGrid = obj as DataGrid;
        if ( dataGrid != null )
        {
          ICommand cmd = (ICommand) args.NewValue;
    
          if ( args.OldValue == null && args.NewValue != null )
          {
            dataGrid.MouseDoubleClick += ExecuteDataGridDoubleClick;
          }
          else if ( args.OldValue != null && args.NewValue == null )
          {
            dataGrid.MouseDoubleClick -= ExecuteDataGridDoubleClick;
          }
        }
      }
    
      private static void ExecuteDataGridDoubleClick(object sender, MouseButtonEventArgs args)
      {
        DependencyObject obj = sender as DependencyObject;
        ICommand cmd = (ICommand) obj.GetValue(DataGridDoubleClickProperty);
        if ( cmd != null )
        {
          if ( cmd.CanExecute(obj) )
          {
            cmd.Execute(obj);
          }
        }
      }
    
    }
    

    In your View you use Binding to map this DependencyProperty to Command

    <Grid DataContext="{StaticResource viewModel}">
      <DataGrid AutoGenerateColumns="True" 
            ItemsSource="{Binding Data}" 
            SelectedItem="{Binding SelectedItem}" 
            clr:Commands.DataGridDoubleClickCommand="{Binding DataGridDoubleClick}"
        />
    </Grid>
    

    DataGridDoubleClick is ICommand property in your ViewModel class

    Hope this help

    Lukas

    • Marked as answer by devBrix Sunday, July 11, 2010 5:57 AM
    Saturday, July 10, 2010 11:00 AM
  • Wow,Just seen.This looks exactly the kind of thing I am looking for.

    It works first time too.THANKS!!!.

    Could you tell me why in Xaml it says "Dependency Property Missing"?

    Really appreaciate your help


    Thanks for your help
    Sunday, July 11, 2010 5:57 AM
  • You're welcome

    So behavior of WPF designer is sometimes unbelievable :-)

    Sunday, July 11, 2010 6:35 AM
  • I realise that the question has been successfully answered but I think the answer is far more complicated than it needs to be, so I thought I'd post the simple way. Maybe someone can spot a flaw in my solution?

    I was struggling with implementing exactly the same scenario: Load a form based on a click (or double click) on a datagrid that has been loaded from  a database.

    The question seemed to be where is the row_click event for the grid? It didn't seem to exist. However it does, but it is an event for each row, so it needs to be set for each row. Very easy...

    Use the datagrids "loadingRow" event to set the RowClick event for each row:

     

    private void datagrid1_LoadingRow(object sender, DataGridRowEventArgs e)
    {
       e.Row.MouseLeftButtonUp +=datagrid1_RowClick;
    }

    then create the event for the row...

    private void datagrid1_RowClick(object sender, MouseButtonEventArgs e)
    {
       DataGridRow row = (DataGridRow)sender;
       // do your stuff
    }

    It's just a very simple OO implementation.

    • Edited by gsd_mike_p Friday, November 05, 2010 7:02 AM adjusted layout
    Friday, November 05, 2010 7:01 AM
  • I realise that the question has been successfully answered but I think the answer is far more complicated than it needs to be, so I thought I'd post the simple way. Maybe someone can spot a flaw in my solution?

    Your solution may work fine.  But I think the question was for a solution using Commands instead of a code behind solution.  Using commands is the preferred way to handle UI events in WPF.
    Friday, March 11, 2011 3:51 PM
  • Super! Thanks.
    Monday, October 10, 2011 11:25 AM
  • I am attempting to use this from a VB project.

    I have the Code above in a C# project that I have been using for other things in XAML.

    What I am having problems with is just what do I need to do on the Viewmodel side coded in VB!  (I hate trying to translate commands/events!)

    Trying to find an example of this stuff in VB is like looking for a needle in a haystack!

    Brian

    Friday, February 10, 2012 3:18 PM
  • Ok, I have this running now.  I can see the event firing and it does everything it is supposed to.  However, I have tab pages, and XAML for some reason fires an event setting the current tabpage (I am changing the selected tab page in the event, and the event works if I call it from a buttonclick!) thus resetting it after I have changed it.  What could be causing this to occur, and why does it work from a button but not from the doubleclick?
    Brian
    Tuesday, April 10, 2012 1:53 PM
  • This Solution works great, and is incredibly easy to adapt to any event. That being said, I've come across a situation where the functionality is just slightly off. I amended this code so that a command triggers on the SelectionChanged event of a combobox. The command is executed when the event triggers, however it executes before the value that the combobox is bound to changes. Here is the code.

    public static readonly DependencyProperty ComboBoxChangedProperty =
            DependencyProperty.RegisterAttached("ComboBoxChangedCommand", typeof(ICommand), typeof(Commands),
                              new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveComboBoxChangedEvent)));

            public static ICommand GetComboBoxChangedCommand(DependencyObject obj)
            {
                return (ICommand)obj.GetValue(ComboBoxChangedProperty);
            }

            public static void SetComboBoxChangedCommand(DependencyObject obj, ICommand value)
            {
                obj.SetValue(ComboBoxChangedProperty, value);
            }

            public static void AttachOrRemoveComboBoxChangedEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
            {
                ComboBox comboBox = obj as ComboBox;
                if (comboBox != null)
                {
                    ICommand cmd = (ICommand)args.NewValue;

                    if (args.OldValue == null && args.NewValue != null)
                    {
                        comboBox.SelectionChanged += ExecuteComboBoxChanged;
                    }
                    else if (args.OldValue != null && args.NewValue == null)
                    {
                        comboBox.SelectionChanged -= ExecuteComboBoxChanged;
                    }
                }
            }

            private static void ExecuteComboBoxChanged(object sender, SelectionChangedEventArgs args)
            {
                DependencyObject obj = sender as DependencyObject;
                ICommand cmd = (ICommand)obj.GetValue(ComboBoxChangedProperty);
                if (cmd != null)
                {
                    if (cmd.CanExecute(obj))
                    {
                        cmd.Execute(obj);
                    }
                }
            }

    For example, if the combobox's value is bound to a string variable in the viewmodel named BoundValue. If the currently selected value is null (the combobox was just instantiated), and it is changed to "item1" the event fires and executes the command. The command references BoundValue which still holds the value of null. If the combobox's value is again changed to "item2", BondValue now holds the value of "item1".

    In short, is there anyway to get the command to execute after bound value is updated?

    Monday, August 06, 2012 3:33 PM
  • I have tried this solution but I can't seem to get i right:

    I keep get a XamlParseException:

    A 'Binding' cannot be used within a 'DataGrid' collection. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

       at MS.Internal.Helper.CheckCanReceiveMarkupExtension(MarkupExtension markupExtension, IProvideValueTarget provideValueTarget, DependencyObject& targetDependencyObject, DependencyProperty& targetDependencyProperty)   at System.Windows.Data.BindingBase.ProvideValue(IServiceProvider serviceProvider)   at Microsoft.Expression.DesignModel.Core.InstanceBuilderOperations.SetValue(Object target, IProperty propertyKey, Object value)   at Microsoft.Expression.DesignModel.InstanceBuilders.ClrObjectInstanceBuilder.ModifyValue(IInstanceBuilderContext context, ViewNode target, IProperty propertyKey, Object value, PropertyModification modification)   at Microsoft.Expression.DesignModel.InstanceBuilders.DependencyObjectInstanceBuilderBase`1.ModifyValue(IInstanceBuilderContext context, ViewNode target, IProperty propertyKey, Object value, PropertyModification modification)   at Microsoft.Expression.Platform.WPF.InstanceBuilders.FrameworkElementInstanceBuilder.ModifyValue(IInstanceBuilderContext context, ViewNode target, IProperty propertyKey, Object value, PropertyModification modification)   at Microsoft.Expression.DesignModel.InstanceBuilders.ClrObjectInstanceBuilder.UpdateProperty(IInstanceBuilderContext context, ViewNode viewNode, IProperty propertyKey, DocumentNode valueNode)

    Here is my xaml for the dataagrid:

    <DataGrid IsReadOnly="True" ItemsSource="{Binding Customers}"

                       AutoGenerateColumns="False"  commands:CommandCollection.DataGridDoubleClickCommand="{BindingcOpenCustomerWindow}" Grid.Column="1" Grid.Row="1" Height="380" HorizontalAlignment="Left" Name="dataGrid1" VerticalAlignment="Top" Width="561">

                <DataGrid.Columns>

                    <DataGridTextColumn Binding="{Binding name}"  Header="Navn"></DataGridTextColumn>

                    <DataGridTextColumn Binding="{Binding address}" Header="Adresse"></DataGridTextColumn>

                    <DataGridTextColumn Binding="{Binding phone1}" Header="Phone#1"></DataGridTextColumn>

                    <DataGridTextColumn Binding="{Binding phone2}" Header="Phone#2"></DataGridTextColumn>

                </DataGrid.Columns>

    </DataGrid>

    ...and here is my Dependency Property:

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    namespace WpfApplicationAE.Commands

    {

        public static class CommandCollection 

        {

            public static readonly DependencyProperty DataGridDoubleClickProperty =

                DependencyProperty.Register("DataGridDoubleClickCommand", typeof(ICommand), typeof(CommandCollection),

                                            new PropertyMetadata(AttachOrRemoveDataGridDoubleClickEvent));

            public static ICommand GetDataGridDoubleClickCommand(DependencyObject obj)

            {

                return (ICommand)obj.GetValue(DataGridDoubleClickProperty);

            }

            public static void SetDataGridDoubleClickCommand(DependencyObject obj, ICommand value)

            {

                obj.SetValue(DataGridDoubleClickProperty, value);

            }

            public static void AttachOrRemoveDataGridDoubleClickEvent(DependencyObject obj,

                                                                      DependencyPropertyChangedEventArgs args)

            {

                var dataGrid = obj as DataGrid;

                if (dataGrid != null)

                {

                    var cmd = (ICommand)args.NewValue;

                    if (args.OldValue == null && args.NewValue != null)

                    {

                        dataGrid.MouseDoubleClick += ExecuteDataGridDoubleClick;

                    }

                    else if (args.OldValue != null && args.NewValue == null)

                    {

                        dataGrid.MouseDoubleClick -= ExecuteDataGridDoubleClick;

                    }

                }

            }

            private static void ExecuteDataGridDoubleClick(object sender, MouseButtonEventArgs args)

            {

                var obj = sender as DependencyObject;

                var cmd = (ICommand)obj.GetValue(DataGridDoubleClickProperty);

                if (cmd != null)

                {

                    if (cmd.CanExecute(obj))

                    {

                        cmd.Execute(obj);

                    }

                }

            }

        }

    }

    I hope someone can help me out.

    sincerly,

    Marius H Enerud

    Friday, September 28, 2012 10:30 AM
  • How to pass command parameter with the given solution?

    Krunal C

    Sunday, October 13, 2013 5:05 AM
  • Hi, Krunal

    Did you get how to pass command parameter in above solutions, because i also have problem in this, if you know then please help me.

    Tuesday, March 11, 2014 1:57 PM
  • How to pass command parameter

    XAML:
    SelectedItem="{Binding SelectedX}"
    clr:Commands.DataGridDoubleClickCommand="{Binding OpenXCommand}"

    ViewModel:

    public X SelectedX { get; set; }
    public ICommand OpenXCommand { get; set; }

    ctor
    {
       OpenXCommand = new RelayCommand(OpenX);
    }

     public void OpenX()
            {

      if (SelectedX != null)
                {
                    //TODO
                    var message = string.Format("{0} selected.", SelectedX.YProperty);
                    System.Windows.MessageBox.Show(message);
                }
    }

    Tuesday, February 14, 2017 4:45 PM