I was trying to implement a dialog box with layout as something like this
I wanted keyboard behavior as
A very easy way is to use <RadioButtons>
to group individual <Radiobutton>
but that is not affordable with the overall project as using the dependency library winui 2+ is messing up with other parts of the code and affecting functionality of some feature.
After researching on internet and reading through this documentation, I tried <ListView>
which was causing two issues
Upon further search, I landed upon this .
Code:
<ContentDialog>
<StackPanel>
<StackPanel XYFocusKeyboardNavigation="Enabled" TabFocusNavigation="Once">
<RadioButton Content="a" XYFocusKeyboardNavigation="Enabled" />
<RadioButton Content="b" XYFocusKeyboardNavigation="Enabled" />
<RadioButton Content="c" XYFocusKeyboardNavigation="Enabled" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Uid="Cancel" Name="CancelButton" Click="Cancel" Margin="5" IsEnabled="False"/>
<Button x:Uid="Save" Name="SaveButton" Click="Save" Margin="5" IsEnabled="False"/>
</StackPanel>
</StackPanel>
</ContentDialog>
Upon implementation, most of the issues with narrator and navigation were gone, except for 1 issue:
The control never lands on 'b' radio button.
Being on 'a' radio button, pressing down arrow key doesn't works. Pressing up arrow key moves the control to 'c'.
Being on 'c' radio button, pressing up arrow key doesn't works. Pressing down arrow key moves the control to 'a'.
I even tried XYFocusDownNavigationStrategy="Projection"
on 'c' radio button and XYFocusUpNavigationStrategy="Projection"
on 'a' radio button, but it didn't work.
How to fix this issue?
Update2
I'm tried to implement what you want a simple demo by handling the PreviewKeyDown Event. We need to find the RadioButtons first and then set focus state to different buttons based on which key the user has pressed.
Xaml:
<ContentDialog x:Name="MyDialog">
<StackPanel>
<StackPanel XYFocusKeyboardNavigation="Enabled" PreviewKeyDown="StackPanel_PreviewKeyDown" TabFocusNavigation="Once">
<RadioButton Content="a" XYFocusKeyboardNavigation="Enabled" />
<RadioButton Content="b" XYFocusKeyboardNavigation="Enabled" />
<RadioButton Content="c" XYFocusKeyboardNavigation="Enabled" />
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Uid="Cancel" Content="Cancel" Name="CancelButton" Margin="5" IsEnabled="True"/>
<Button x:Uid="Save" Content="Save" Name="SaveButton" Margin="5" IsEnabled="True"/>
</StackPanel>
</StackPanel>
</ContentDialog>
Code-behind:
//radiobuttons
public List<RadioButton> radioButtons { get; set; }
// focus index
public int focusindex = -1;
public MainPage()
{
this.InitializeComponent();
radioButtons = new List<RadioButton>();
this.Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
//get all the radiobuttons inside the contentdialog
DependencyObject contentObject = MyDialog.Content as DependencyObject;
FindRadioButtons(contentObject, radioButtons);
}
private void StackPanel_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
// handle the arrow key navigation
if (e.Key == Windows.System.VirtualKey.Up)
{
e.Handled = true;
//check the focus index and make the correct radio button focus
focusindex = focusindex < 1 ? 2 : focusindex - 1;
RadioButton radioButton = radioButtons[focusindex];
radioButton.Focus(FocusState.Keyboard);
Debug.WriteLine("Up pressed");
}
else if (e.Key == Windows.System.VirtualKey.Down)
{
e.Handled = true;
//check the focus index and make the correct radio button focus
focusindex = focusindex > 1 ? 0 : focusindex +1;
RadioButton radioButton = radioButtons[focusindex];
radioButton.Focus(FocusState.Keyboard);
Debug.WriteLine("Down pressed");
}
else if (e.Key == Windows.System.VirtualKey.Left)
{
e.Handled = true;
//check the focus index and make the correct radio button focus
focusindex = focusindex < 1 ? 2 : focusindex - 1;
RadioButton radioButton = radioButtons[focusindex];
radioButton.Focus(FocusState.Keyboard);
Debug.WriteLine("Left pressed");
}
else if (e.Key == Windows.System.VirtualKey.Right)
{
e.Handled = true;
//check the focus index and make the correct radio button focus
focusindex = focusindex > 1 ? 0 : focusindex + 1;
RadioButton radioButton = radioButtons[focusindex];
radioButton.Focus(FocusState.Keyboard);
Debug.WriteLine("Right pressed");
}
else if (e.Key == Windows.System.VirtualKey.Tab)
{
//When the tab pressed, the first item will always be focused
focusindex = 0;
Debug.WriteLine("Tab pressed");
}
}
public void FindRadioButtons(DependencyObject parant, List<RadioButton> list)
{
int count = VisualTreeHelper.GetChildrenCount(parant);
for (int i = 0; i < count; i++)
{
var MyChild = VisualTreeHelper.GetChild(parant, i);
if (MyChild is FrameworkElement && MyChild.GetType() == typeof(RadioButton))
{
list.Add((RadioButton)MyChild);
}
else
{
FindRadioButtons(MyChild,list);
}
}
}
Update:
I've checked the source code of the winui RadioButtons to see how it implements the function. I found that it's handling the keydown event of the root panel so it could manually control the arrow key navigation logic. I think you might need to do the same in your UWP app by handling the keydown event of the StackPanel or contentdialog
Old reply:
I'd suggest that you could try the RadioButtons control in the WinUi 2.8 for UWP. It could meet all your requirement.
Here is the link about how to get started with WinUI2 in UWP: Getting started with the Windows UI 2 Library.