@@ -511,20 +511,27 @@ private static void EnableCellEditAssistPropertyChangedCallback(DependencyObject
511511 {
512512 if ( d is DataGrid dataGrid && e . OldValue != e . NewValue && e . NewValue is bool isEnabled )
513513 {
514- dataGrid . PreviewMouseLeftButtonDown -= DataGridOnPreviewMouseLeftButtonDown ;
514+ dataGrid . RemoveHandler ( UIElement . MouseLeftButtonDownEvent , ( RoutedEventHandler ) DataGridOnMouseLeftButtonDown ) ;
515+ dataGrid . PreviewKeyDown -= EditOnSpaceBarPress ;
516+
515517 if ( isEnabled )
516518 {
517- dataGrid . PreviewMouseLeftButtonDown += DataGridOnPreviewMouseLeftButtonDown ;
519+ // Register for bubbling events from cells, even when the cell handles them (thus the 'true' parameter)
520+ dataGrid . AddHandler ( UIElement . MouseLeftButtonDownEvent , ( RoutedEventHandler ) DataGridOnMouseLeftButtonDown , true ) ;
521+ dataGrid . PreviewKeyDown += EditOnSpaceBarPress ;
518522 }
519523 }
520524 }
521525
526+ // This relay is only needed because the UIElement.AddHandler() has strict requirements for the signature of the passed Delegate
527+ private static void DataGridOnMouseLeftButtonDown ( object sender , RoutedEventArgs e ) => AllowDirectEditWithoutFocus ( sender , ( MouseButtonEventArgs ) e ) ;
528+
522529 /// <summary>
523530 /// Allows editing of components inside of a <see cref="DataGrid"/> cell with a single left click.
524531 /// </summary>
525- private static void DataGridOnPreviewMouseLeftButtonDown ( object sender , MouseButtonEventArgs e )
532+ private static void AllowDirectEditWithoutFocus ( object sender , MouseButtonEventArgs mouseArgs )
526533 {
527- var originalSource = ( DependencyObject ) e . OriginalSource ;
534+ var originalSource = ( DependencyObject ) mouseArgs . OriginalSource ;
528535 var dataGridCell = originalSource
529536 . GetVisualAncestry ( )
530537 . OfType < DataGridCell > ( )
@@ -541,49 +548,94 @@ private static void DataGridOnPreviewMouseLeftButtonDown(object sender, MouseBut
541548 {
542549 var dataGrid = ( DataGrid ) sender ;
543550
544- // Check if the cursor actually hit the element and not just the complete cell
545- var mousePosition = e . GetPosition ( element ) ;
546- var elementHitBox = new Rect ( element . RenderSize ) ;
547- if ( elementHitBox . Contains ( mousePosition ) )
551+ // If it is a DataGridTemplateColumn we want the
552+ // click to be handled naturally by the control
553+ if ( dataGridCell . Column . GetType ( ) == typeof ( DataGridTemplateColumn ) )
554+ {
555+ return ;
556+ }
557+
558+ // If the cell is already being edited, we let the cell itself handle the mouse event
559+ if ( dataGridCell . IsEditing )
560+ {
561+ return ;
562+ }
563+
564+ // Don't handle events from nested data grids
565+ var parentDataGrid = dataGridCell
566+ . GetVisualAncestry ( )
567+ . OfType < DataGrid > ( )
568+ . FirstOrDefault ( ) ;
569+ if ( parentDataGrid != dataGrid )
548570 {
549- // If it is a DataGridTemplateColumn we want the
550- // click to be handled naturally by the control
551- if ( dataGridCell . Column . GetType ( ) == typeof ( DataGridTemplateColumn ) )
571+ return ;
572+ }
573+
574+ dataGrid . CurrentCell = new DataGridCellInfo ( dataGridCell ) ;
575+ dataGrid . BeginEdit ( ) ;
576+
577+ // Now use the content from the editable template/style
578+ switch ( dataGridCell . Content )
579+ {
580+ case TextBoxBase textBox :
552581 {
553- return ;
554- }
582+ if ( TextBoxHelper . GetSelectAllOnFocus ( textBox ) == false )
583+ {
584+ // Send a 'left-click' routed event to the TextBox to place the I-beam at the position of the mouse cursor
585+ var mouseDownEvent = new MouseButtonEventArgs ( mouseArgs . MouseDevice , mouseArgs . Timestamp , mouseArgs . ChangedButton )
586+ {
587+ RoutedEvent = Mouse . MouseDownEvent ,
588+ Source = mouseArgs . Source
589+ } ;
590+ textBox . RaiseEvent ( mouseDownEvent ) ;
591+ }
555592
556- dataGrid . CurrentCell = new DataGridCellInfo ( dataGridCell ) ;
557- dataGrid . BeginEdit ( ) ;
593+ break ;
594+ }
558595
559- // Now use the content from the editable template/style
560- switch ( dataGridCell . Content )
596+ case ToggleButton toggleButton :
561597 {
562- // Send a 'left click' routed command to the toggleButton
563- case ToggleButton toggleButton :
598+ // Check if the cursor actually hit the checkbox and not just the cell
599+ var mousePosition = mouseArgs . GetPosition ( element ) ;
600+ var elementHitBox = new Rect ( element . RenderSize ) ;
601+ if ( elementHitBox . Contains ( mousePosition ) )
564602 {
565- var newMouseEvent = new MouseButtonEventArgs ( e . MouseDevice , 0 , MouseButton . Left )
603+ // Send a 'left click' routed command to the toggleButton to toggle the state
604+ var newMouseEvent = new MouseButtonEventArgs ( mouseArgs . MouseDevice , mouseArgs . Timestamp , mouseArgs . ChangedButton )
566605 {
567606 RoutedEvent = Mouse . MouseDownEvent ,
568607 Source = dataGrid
569608 } ;
570609
571610 toggleButton . RaiseEvent ( newMouseEvent ) ;
572- break ;
573611 }
574612
575- // Open the dropdown explicitly. Left clicking is not viable, as it would edit the text and not open the dropdown
576- case ComboBox comboBox :
577- {
578- comboBox . IsDropDownOpen = true ;
579- e . Handled = true ;
580- break ;
581- }
613+ break ;
614+ }
615+
616+ // Open the dropdown explicitly. Left clicking is not viable, as it would edit the text and not open the dropdown
617+ case ComboBox comboBox :
618+ {
619+ comboBox . IsDropDownOpen = true ;
620+
621+ break ;
582622 }
583623 }
584624 }
585625 }
586626
627+ private static void EditOnSpaceBarPress ( object sender , KeyEventArgs e )
628+ {
629+ if ( sender is DataGrid dataGrid )
630+ {
631+ if ( e is { Key : Key . Space , OriginalSource : DataGridCell { IsReadOnly : false , Column : DataGridComboBoxColumn } } )
632+ {
633+ dataGrid . BeginEdit ( ) ;
634+ e . Handled = true ;
635+ }
636+ }
637+ }
638+
587639 public static readonly DependencyProperty SelectionUnitProperty
588640 = DependencyProperty . RegisterAttached (
589641 "SelectionUnit" ,
0 commit comments