Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UWP - Don't fire Click event when pressing space (like in Movies & TV app)

Tags:

c#

xaml

uwp

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:

  • Need to add if in every Click Handler
  • There is button click animation...

Solution 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

  • It change focus from disabled button. Need to change it back after enable. Focus is jumping..
  • Disable and enable animation appear
  • it is too hackie

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?

like image 595
Alamakanambra Avatar asked Jun 25 '17 17:06

Alamakanambra


1 Answers

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 AppBarButtons (don't forget buttons inside Flyouts 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.


Update

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 AppBarButtons 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.

like image 103
Justin XL Avatar answered Oct 23 '22 13:10

Justin XL