I finally got the best answer to this problem that I have been struggling with for a long time.
At some point, I confirmed that pressing Ctrl+Tab in the DataGrid would result in the desired behavior.
From this, I thought I could just swap the values of KeyboardNavigation.TabNavigation attached property and KeyboardNavigation.ControlTabNavigation attached property in the DataGrid.
However, it did not work.
After checking the code of the DataGrid's child elements with ReferenceSource, further, I thought that I should swap the values of KeyboardNavigation.TabNavigation attached property and KeyboardNavigation.ControlTabNavigation attached property in DataGridCellsPanel.
I ran it and got the ideal behavior.
What's more, and this is great, the current cell after focus is reentered is the current cell when the focus is lost.
<DataGrid KeyboardNavigation.TabNavigation="Once"
KeyboardNavigation.ControlTabNavigation="Continue">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<DataGridCellsPanel KeyboardNavigation.TabNavigation="Continue"
KeyboardNavigation.ControlTabNavigation="Local"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
It can also be realized with the following attached properties, referring to the StandardTab property of DataGridView in WinForms.
For VB.net
Public Class DataGridHelper
Public Shared Function GetStandardTab(element As DataGrid) As Boolean
If element Is Nothing Then Throw New ArgumentNullException(NameOf(element))
Return CBool(element.GetValue(StandardTabProperty))
End Function
Public Shared Sub SetStandardTab(element As DataGrid, value As Boolean)
If element Is Nothing Then Throw New ArgumentNullException(NameOf(element))
element.SetValue(StandardTabProperty, value)
End Sub
Public Shared ReadOnly StandardTabProperty As DependencyProperty =
DependencyProperty.RegisterAttached("StandardTab",
GetType(Boolean), GetType(DataGridHelper),
New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.Inherits, New PropertyChangedCallback(AddressOf OnStandardTabPropertyChanged)))
Private Shared Sub OnStandardTabPropertyChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
If TypeOf d Is DataGrid OrElse TypeOf d Is DataGridCellsPanel Then
If CBool(e.NewValue) Then
If TypeOf d Is DataGridCellsPanel Then
d.SetValue(KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Continue)
d.SetValue(KeyboardNavigation.ControlTabNavigationProperty, KeyboardNavigationMode.Local)
Else
d.SetValue(KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Once)
d.SetValue(KeyboardNavigation.ControlTabNavigationProperty, KeyboardNavigationMode.Continue)
End If
Else
d.ClearValue(KeyboardNavigation.TabNavigationProperty)
d.ClearValue(KeyboardNavigation.ControlTabNavigationProperty)
End If
End If
End Sub
End Class
For C# (not tested)
public class DataGridHelper
{
public static bool GetStandardTab(DataGrid element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return (bool)element.GetValue(StandardTabProperty);
}
public static void SetStandardTab(DataGrid element, bool value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(StandardTabProperty, value);
}
public static readonly DependencyProperty StandardTabProperty =
DependencyProperty.RegisterAttached("StandardTab",
typeof(bool), typeof(DataGridHelper),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnStandardTabPropertyChanged)));
private static void OnStandardTabPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DataGrid || d is DataGridCellsPanel)
{
if ((bool) e.NewValue)
{
if (d is DataGridCellsPanel)
{
d.SetValue(KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Continue);
d.SetValue(KeyboardNavigation.ControlTabNavigationProperty, KeyboardNavigationMode.Local);
}
else
{
d.SetValue(KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Once);
d.SetValue(KeyboardNavigation.ControlTabNavigationProperty, KeyboardNavigationMode.Continue);
}
}
else
{
d.ClearValue(KeyboardNavigation.TabNavigationProperty);
d.ClearValue(KeyboardNavigation.ControlTabNavigationProperty);
}
}
}
}