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