Detecting Double Click Events on the WPF DataGrid
March 21st, 2009
Either I missed the obvious solution, or there is indeed no simple way to catch double click events on a given row of Microsoft’s WPF DataGrid.
This snippet here fires the event whenever the grid is clicked – it doesn’t matter whether the user double-clicks into an empty area (no rows) or a row:
<!-- fires even if the user does not click a given row --> <dg:DataGrid dg:DataGridRow.MouseDoubleClick="OnDoubleClick" />
As a result, I reverted to a workaround by searching the Visual Tree of the event source for an instance of type DataGridRow. In order to find the ancestor item, I used a snippet I posted here a while ago. Here’s the full code:
XAML:
<!-- just register a listener on the grid --> <dg:DataGrid MouseDoubleClick="OnDoubleClick" />
Code-Behind:
/// <summary> /// Handles double-clicks on datagrid rows. /// </summary> private void OnDoubleClick(object sender, MouseButtonEventArgs e) { //search the object hierarchy for a datagrid row DependencyObject source = (DependencyObject) e.OriginalSource; var row = UIHelpers.TryFindParent<DataGridRow>(source); //the user did not click on a row if (row == null) return; //[insert great code here...] e.Handled = true; }
UIHelper class providing the TryFindParent method:
(Snippet updated: 2009.09.14)
/// <summary> /// Finds a parent of a given item on the visual tree. /// </summary> /// <typeparam name="T">The type of the queried item.</typeparam> /// <param name="child">A direct or indirect child of the /// queried item.</param> /// <returns>The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned.</returns> public static T TryFindParent<T>(this DependencyObject child) where T : DependencyObject { //get parent item DependencyObject parentObject = GetParentObject(child); //we've reached the end of the tree if (parentObject == null) return null; //check if the parent matches the type we're looking for T parent = parentObject as T; if (parent != null) { return parent; } else { //use recursion to proceed with next level return TryFindParent<T>(parentObject); } } /// <summary> /// This method is an alternative to WPF's /// <see cref="VisualTreeHelper.GetParent"/> method, which also /// supports content elements. Keep in mind that for content element, /// this method falls back to the logical tree of the element! /// </summary> /// <param name="child">The item to be processed.</param> /// <returns>The submitted item's parent, if available. Otherwise /// null.</returns> public static DependencyObject GetParentObject(this DependencyObject child) { if (child == null) return null; //handle content elements separately ContentElement contentElement = child as ContentElement; if (contentElement != null) { DependencyObject parent = ContentOperations.GetParent(contentElement); if (parent != null) return parent; FrameworkContentElement fce = contentElement as FrameworkContentElement; return fce != null ? fce.Parent : null; } //also try searching for parent in framework elements (such as DockPanel, etc) FrameworkElement frameworkElement = child as FrameworkElement; if (frameworkElement != null) { DependencyObject parent = frameworkElement.Parent; if (parent != null) return parent; } //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper return VisualTreeHelper.GetParent(child); }
Enjoy 🙂
Thanks a lot man 🙂 It works out great…..
Using the WPF datagrid for the first time and even with the Windows form datagrid the event would fire no matter where the doubleclick was done.
I found that the following “if” was all I needed to be check to see if a row was selected.
private void datagrid1_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed & datagrid1.SelectedItem != null)
AC,
Your solution also fires the event if you have a selected row and click on an empty area of the grid. Be careful with this one.
Furthermore, you don’t get the row, but the bound item 😉
works out great with ListView too :
var row = TryFindParent(source);
And for databinding adepts, you still can catch your ObjectViewModel :
ObjectViewModel object = row.Content as ObjectViewModel;
Thanks a lot,
Cam
I’ve found an alternative for this which probably wont scale very well but appears to work for my DataGrid. Returns the underlying data bound object of the row under the mouse click:
private T TryGetDataBoundItem(MouseButtonEventArgs e)
{
try
{
if (e.OriginalSource is FrameworkElement)
{
if (((FrameworkElement)(e.OriginalSource)).DataContext is T)
return (T)((FrameworkElement)(e.OriginalSource)).DataContext;
}
}
catch
{
}
return default(T);
}
Excellent Posting! Thanks for doing this!
Nice code. Thank you.
I found that this method works like a charm for me:
private void dgMyGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
// Check if the user double-clicked a grid row and not something else
DataGridRow row = ItemsControl.ContainerFromElement((DataGrid)sender, e.OriginalSource as DependencyObject) as DataGridRow;
// If so, go ahead and do my thing
if (row != null) BlahBlahBlah();
}
Seems pretty simple and elegant. HTH!
Good Mike Loux,
your solution is the best.
@Mike Loux, please post the xaml definition of your grid, thank you.
How to get the cell value from the selected row
The following just works! Try it!
Heres another very easy solution:
For the Datagrid set a LoadingRow event.
Use that to attach an event to each row:
(I’ve shown a MouseLeftButtonUP but it could be any mouse event.)
private void dgMyGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.MouseLeftButtonUp +=MyGrid_RowClick;
}
then create the MyGrid_RowClick event:
private void MyGrid_RowClick(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
if (sender is DataGridRow)
{
//do your stuff
}
}
Why no just use PreviewMouseDoubleClick?
Let’s say you bound a bunch of Thing objects to your grid
Then…
void thingsGrid_RowDoubleClick(object sender, MouseButtonEventArgs e)
{
if (thingsGrid.SelectedItem != null)
{
Thing thing = (Thing)dealersGrid.SelectedItem;
MessageBox.Show(thing.SomeProperty);
}
}
@shodson
This blog post starts with that very event. Unless the API changes (it’s quite an old post), your event will also fire if you click into the empty area of the grid (if it only contains a few rows).
You might also look at the solution from Mike Loux (see comments above).
Cheers,
Philipp
To find a cell:
private void UpdateItemRow(DownloadCtlrStatus dlStatus)
{
// get the row
DataGridRow row = (DataGridRow)datagrid1.ItemContainerGenerator.ContainerFromItem(dlStatus);
if (row == null) return;
// get the Cells
DataGridCellsPresenter cellPresenter = FindVisualChild(row);
// cell0
DataGridCell cell = (DataGridCell)cellPresenter.ItemContainerGenerator.ContainerFromIndex(0);
Image img = FindVisualChild(cell);
if (img != null)
img.GetBindingExpression(Image.SourceProperty).UpdateTarget();
// cell10
cell = (DataGridCell)cellPresenter.ItemContainerGenerator.ContainerFromIndex(9);
TextBlock text = (TextBlock)cell.Content;
text.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
}
FindVisualChild is a static helper function you can find on MSDN.
@Mike Loux
nice
it works for me
@Mike Loux
nice
it works for me
but how to use this to edit that record we double clicked
& how to get values of that rows
hi
wow
this was excellent.
thanks a lot.
Thank you for the helpful article. I spent a while to find some useful material about DataGrid Mouse Double Click Event.
Hi Folks, try this out:
Note: This works out for datagrids with whose binding source is a binding
list of objects ex: a binding list of employees with public properties
//create an event handler and hook up a delegate to it
private System.Windows.Input.MouseButtonEventHandler populateTextBox;
populateTextBox += doSOmething;
dataGrid1.MouseDoubleClick+= new System.Windows.Input.MouseButtonEventHandler( populateTextBox);
//Now the Code :
private void doSOmething(object sender,System.Windows.Input.MouseButtonEventArgs e)
{
Class1 selecteditem = selectedItem as Class1;
if (selecteditem != null)
{
//your logic here
label1.Text = selectedItem.Age + ” “;
}
}
@Mike Loux
Love the simplicity and elegance. Thanks so much!
Hi,
I have come across this situation and need help, so in my wpf window there are two grids and a user control that we can drag. So i want that when its on grid1 it does thing1 and when its on grid2 it does thing2. How to implement this?
simplest way to achieve this
private void dgResult_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
}
@Mike Loux
perfect ! Thanks Loux work fine for me 😉
Worked Great wasted hours to get a solution working before seing yours . Thank you so much !