I am working on custom media player and trying to reproduce same behavior as in Movies & TV app (Windows 10 CU).
Space is used to Play & Pause video no matter what. Space is not used to click buttons when they are focused (but Enter is). This behavior breaks some rules about keyboard accessibility, but I think it is OK. Space for Play & Pause is something that user expect.
Question is: How they did it?
I found some half-solutions:
Solution 1 Window.Current.CoreWindow.KeyDown
and if
in Click Event Handler
Page.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Window.Current.CoreWindow.KeyDown += CoreWindowOnKeyDown;
//...
}
bool isItSpace;
private void CoreWindowOnKeyDown(CoreWindow sender, KeyEventArgs args)
{
if (args.VirtualKey == VirtualKey.Space)
isItSpace = true;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (isItSpace)
{
isItSpace = false;
return;
}
//...
}
Page.xaml:
<Button Click="ButtonBase_OnClick" >Button Text</Button>
Why not:
if
in every Click HandlerSolution 2 Disable focused Button in Window.Current.CoreWindow.KeyDown
and enable it in KeyUp
Something like:
if (FocusManager.GetFocusedElement() is Button)
{
var bu = (Button)FocusManager.GetFocusedElement();
bu.IsEnabled = false;
}
Why not
Solution 3 button.ClickMode = ClickMode.Hover
When ClickMode
is set to Hover
it is not possible to Click it with keyboard. When I change it in KeyDown
handler of Window (like solution 2) it still bubble to Click event for the first time. Solution could be set all buttons to Hover
, but in that case Enter will not cause the Click and I need to change all buttons...
Are there any better solutions on your mind?
This issue comes down to, when you click on a button from the media command bar, it automatically gets focused, and keybaord keys like Enter or Space will automatically trigger the action of the button.
So to solve the issue, you just need to find a way to remove the auto-focus from them.
Prior to Windows 10 build 14393, you can try manually setting the focus to some other element after the click, but this feels like a hack to me.
In 14393, they introduced a new property called AllowFocusOnInteraction
which can help you in this particular case. Basically, what this does is to not auto-focus a control after the user interacts with it, so the focus will remain on the currently focused element.
To apply this, you will need to obtain the default style of the MediaTransportControls control, where you can have access to all the media command bar buttons.
Then you just attach AllowFocusOnInteraction="False"
to all the AppBarButton
s (don't forget buttons inside Flyout
s such as AudioMuteButton
!) except the PlayPauseButton
.
I would even go one step further to manually set the focus on the PlayPauseButton
when the page is initially loaded. So after that whenever the Space key is hit, you will see a nice pressed visual on this button to indicate the play/pause action has been invoked.
Please also note doing this will not affect keybaord navigation. If the user wants, he can still use the Tab key to navigate between buttons. So general user accessibility is not affected.
I personally would like any focused button to react on Space key press, but if you want the absolute same visual behavior as in the offical Films & TV app, that's possible too.
Rather than writting a lot of code-behind, let's create an ExtendedAppBarButton
that encapsulates all the logic.
The idea is very similar with your Solution 2, we will use
AddHandler(KeyDownEvent, new KeyEventHandler(OnKeyDown), true);
AddHandler(KeyUpEvent, new KeyEventHandler(OnKeyUp), true);
to toggle IsEnabled
whenever the Space key is pressed. Also, we will have a special IsPlayPauseButton
dependency property that subscribes to the KeyDown
event of the Window
to manually invoke the button click, whenever it's set to True
.
Inside the default style of our ExtendedAppBarButton
, we need to make sure we have the following set properly -
<Setter Property="Width" Value="{ThemeResource MTCMediaButtonWidth}" />
<Setter Property="Height" Value="{ThemeResource MTCMediaButtonHeight}" />
<Setter Property="AllowFocusOnInteraction" Value="False" />
<Setter Property="AllowFocusWhenDisabled" Value="True" />
<Setter Property="UseSystemFocusVisuals" Value="False" />
Note the UseSystemFocusVisuals
is set to False
'cause we are creating our own focus visual to avoid the system one flashing issue that you have already noticed.
<Rectangle x:Name="FocusVisual"
Opacity="0"
StrokeThickness="2"
Stroke="{TemplateBinding BorderBrush}" />
Also, we will remove the Storybaord
inside the Disabled
state as we don't want any animation to happen between the IsEnabled
property changes.
Finally, you will need to obtain the default style of MediaTransportControls
and replace all the AppBarButton
s with the one we just created.
There's a lot of stuff going on here, but once you have everything setup, you don't need to worry about handling all these on your media pages ever.
I have created a simple demo project here in case anything is unclear. Feel free to check it out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With