CodePudding user response:
The Datagridcell's content is a ContentPresenter
control. Your TextBoxes are actually in two different ContentPresenter
controls. So you won't be able to navigate to another cell via Tab. You could test in a simple ListView, put multiple TextBoxes inside a template, and set the TabIndex
property. When you press the TAB key, you could find that only the TextBoxes inside the same item will be focused.
CodePudding user response:
Something else like the DataGrid
is currently out of the question for me. Could not implement my project with a simple ListView
.
I have now created a workaround where I have full control over all UIElement
that are to be focused at all.
In addition, the cursor is set accordingly for the TextBoxes
, should there already be text.
private void DataGrid_OnLoaded(object sender, RoutedEventArgs e)
{
// get parent ContentDialog
var dialog = this.FindVisualParent<ContentDialog>();
// get all children and sort them
// https://github.com/microsoft/microsoft-ui-xaml/blob/548cc630f37eac2658332a5f808160b2cf9f8cef/dev/ContentDialog/ContentDialog_themeresources.xaml#L311
var uiElements = new List<UIElement>();
dialog.FindVisualChildren(uiElements);
var sorted = uiElements.OfType<TextBox>().Where(box => box.TabIndex > 0).Cast<UIElement>().Concat(uiElements.OfType<Button>().Where(button => button.Name.Equals("PrimaryButton") || button.Name.Equals("SecondaryButton") || button.Name.Equals("CloseButton"))).ToList();
// catch tab keyboard event
dialog.PreviewKeyDown = (o, args) =>
{
if (args.Key == VirtualKey.Tab)
{
var currentFocus = sorted.FirstOrDefault(element => element.FocusState != FocusState.Unfocused);
if (currentFocus != null)
{
var nextOf = currentFocus;
n:
var next = sorted.NextOrFirstOf(nextOf);
if (Focus(next) == false) // can happen if a button is not visible
{
nextOf = next;
goto n;
}
}
else
{
Focus(sorted.First());
}
args.Handled = true;
}
};
// focus the first empty TextBox if present
DispatcherQueue.TryEnqueue(() =>
{
var textBox = sorted.OfType<TextBox>().OrderBy(box => box.Text).First();
Focus(textBox);
});
}
private bool Focus(UIElement element)
{
if (element is TextBox textBox)
textBox.SelectionStart = textBox.Text.Length;
return element.Focus(FocusState.Programmatic);
}
DependencyObjectExtensions
public static class DependencyObjectExtensions
{
/// <summary>
/// Find all children by using the <see cref="VisualTreeHelper"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="startNode"></param>
/// <param name="results"></param>
public static void FindVisualChildren<T>(this DependencyObject startNode, List<T> results)
where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(startNode);
for (int i = 0; i < count; i )
{
var current = VisualTreeHelper.GetChild(startNode, i);
if (current.GetType() == typeof(T) || current.GetType().GetTypeInfo().IsSubclassOf(typeof(T)))
{
var asType = (T)current;
results.Add(asType);
}
current.FindVisualChildren(results);
}
}
/// <summary>
/// Find the parent <see cref="DependencyObject"/> by using the <see cref="VisualTreeHelper"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="startNode"></param>
/// <returns></returns>
public static T FindVisualParent<T>(this DependencyObject startNode) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(startNode);
if (parent != null)
{
if (parent.GetType() == typeof(T) || parent.GetType().GetTypeInfo().IsSubclassOf(typeof(T)))
{
return (T)parent;
}
else
{
return parent.FindVisualParent<T>();
}
}
return null;
}
}