Spostamento dello stato attivo con tastiera, game pad, telecomando e strumenti di accessibilità

Keyboard, remote, and D-pad

Usare lo spostamento dello stato attivo per fornire esperienze di interazione coerenti e complete nelle app Windows e controlli personalizzati per gli utenti esperti della tastiera, per gli utenti con disabilità o altri requisiti di accessibilità, nonché la cosiddetta "10-foot experience" degli schermi televisivi e di Xbox One.

Panoramica

Lo spostamento dello stato attivo fa riferimento al meccanismo sottostante che consente agli utenti di spostarsi e interagire con l'interfaccia utente di un'applicazione Windows usando una tastiera, un game pad o un telecomando.

Nota

I dispositivi di input vengono in genere classificati come dispositivi di puntamento, ad esempio tocco, touchpad, penna e mouse, e dispositivi non di puntamento, ad esempio tastiera, game pad e telecomando.

Questo argomento descrive come ottimizzare un'applicazione Windows e creare esperienze di interazione personalizzate per gli utenti usano tipi di input non di puntamento.

Anche ci si concentra sull'input da tastiera per i controlli personalizzati nelle app Windows per PC, un'esperienza di tastiera ben progettata è importante anche per le tastiere software come la tastiera virtuale e la tastiera su schermo (OSK), che supportano strumenti di accessibilità come l'Assistente vocale di Windows e la "10-foot experience".

Vedere Gestire l'input del puntatore per indicazioni sulla creazione di esperienze personalizzate nelle applicazioni Windows per i dispositivi di puntamento.

Per informazioni più generali sulla creazione di app ed esperienze per la tastiera, vedere Interazione tramite tastiera.

Indicazioni generali

Solo gli elementi dell'interfaccia utente che richiedono interazione utente devono supportare lo spostamento dello stato attivo, gli elementi che non richiedono un'azione, ad esempio le immagini statiche, non necessitano dello stato attivo della tastiera. Le utilità per la lettura dello schermo e strumenti di accessibilità simili annunciano ancora questi elementi statici, anche quando non sono inclusi nello spostamento dello stato attivo.

È importante ricordare che, a differenza dello spostamento con un dispositivo di puntamento, ad esempio il mouse o il tocco, lo spostamento dello stato attivo è lineare. Quando si implementa lo spostamento dello stato attivo, considerare il modo in cui un utente interagirà con l'applicazione e quale dovrebbe essere lo spostamento logico. Nella maggior parte dei casi, è consigliabile che il comportamento di spostamento dello stato attivo personalizzato segua lo schema di lettura preferito delle impostazioni cultura dell'utente.

Altre considerazioni sullo spostamento dello stato attivo includono:

  • I controlli sono raggruppati logicamente?
  • Esistono gruppi di controlli con maggiore importanza?
    • In caso affermativo, questi gruppi contengono sottogruppi?
  • Il layout richiede spostamento direzionale personalizzato (tasti di direzione) e l'ordine di tabulazione?

L'eBook Software di progettazione per l'accessibilità contiene un ottimo capitolo su Progettazione della gerarchia logica.

Spostamento direzionale 2D per la tastiera

L'area di spostamento interna 2D di un controllo, o gruppo di controlli, viene definita "area direzionale". Quando lo stato attivo passa a questo oggetto, i tasti di direzione della tastiera (sinistra, destra, su e giù) possono essere usati per spostarsi tra elementi figlio all'interno dell'area direzionale.

directional areaArea di spostamento interna 2D, o area direzionale, di un gruppo di controlli

È possibile usare la proprietà XYFocusKeyboardNavigation (i cui valori possibili sono Auto, Enabled o Disabled) per gestire lo spostamento interno 2D con i tasti di direzione della tastiera.

Nota

L'ordine di tabulazione non è interessato da questa proprietà. Per evitare un'esperienza di spostamento confusa, è preferibile che gli elementi figlio di un'area direzionale non vengano specificati in modo esplicito nell'ordine di spostamento tramite tabulazioni dell'applicazione. Per altri dettagli sul comportamento di tabulazione per un elemento, vedere le proprietà UIElement.TabFocusNavigation e TabIndex.

Auto (comportamento predefinito)

Se impostato su Auto, il comportamento di spostamento direzionale è determinato dalla gerarchia di ereditarietà o dall'origine dell'elemento. Se tutti i predecessori sono in modalità predefinita (impostati su Auto), lo spostamento direzionale con la tastiera non è supportato.

Disabilitato

Impostare XYFocusKeyboardNavigation su Disabled per bloccare lo spostamento direzionale sul controllo e sui relativi elementi figlio.

XYFocusKeyboardNavigation disabled behaviorComportamento disabilitato XYFocusKeyboardNavigation

In questo esempio, per l'elemento StackPanel (ContainerPrimary), XYFocusKeyboardNavigation è impostato su Enabled. Tutti gli elementi figlio ereditano questa impostazione ed è possibile spostarsi su di essi con i tasti di direzione. Tuttavia, gli elementi B3 e B4 si trovano in un elemento StackPanel (ContainerSecondary) secondario con XYFocusKeyboardNavigation impostato su Disabled, che esegue l'override del contenitore primario e disabilita lo spostamento con tasti di direzione su se stesso e tra i relativi elementi figlio.

<Grid 
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" 
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="75"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
                Grid.Row="0" 
                FontWeight="ExtraBold" 
                HorizontalTextAlignment="Center"
                TextWrapping="Wrap" 
                Padding="10" />
    <StackPanel Name="ContainerPrimary" 
                XYFocusKeyboardNavigation="Enabled" 
                KeyDown="ContainerPrimary_KeyDown" 
                Orientation="Horizontal" 
                BorderBrush="Green" 
                BorderThickness="2" 
                Grid.Row="1" 
                Padding="10" 
                MaxWidth="200">
        <Button Name="B1" 
                Content="B1" 
                GettingFocus="Btn_GettingFocus" />
        <Button Name="B2" 
                Content="B2" 
                GettingFocus="Btn_GettingFocus" />
        <StackPanel Name="ContainerSecondary" 
                    XYFocusKeyboardNavigation="Disabled" 
                    Orientation="Horizontal" 
                    BorderBrush="Red" 
                    BorderThickness="2">
            <Button Name="B3" 
                    Content="B3" 
                    GettingFocus="Btn_GettingFocus" />
            <Button Name="B4" 
                    Content="B4" 
                    GettingFocus="Btn_GettingFocus" />
        </StackPanel>
    </StackPanel>
</Grid>

Abilitato

Impostare XYFocusKeyboardNavigation su Enabled per supportare lo spostamento direzionale 2D su un controllo e su ognuno dei relativi oggetti figlio UIElement.

Se impostato, lo spostamento con i tasti di direzione è limitato agli elementi all'interno dell'area direzionale. Lo spostamento tramite tabulazioni non è interessato, poiché tutti i controlli rimangono accessibili tramite la gerarchia dell'ordine di di tabulazione.

XYFocusKeyboardNavigation enabled behaviorComportamento abilitato di XYFocusKeyboardNavigation

In questo esempio, per l'elemento StackPanel (ContainerPrimary), XYFocusKeyboardNavigation è impostato su Enabled. Tutti gli elementi figlio ereditano questa impostazione ed è possibile spostarsi su di essi con i tasti di direzione. Gli elementi B3 e B4 si trovano in un elemento StackPanel (ContainerSecondary) secondario in cui XYFocusKeyboardNavigation non è impostato, che quindi eredita l'impostazione del contenitore primario. L'elemento B5 non si trova all'interno di un'area direzionale dichiarata e non supporta lo spostamento con i tasti di direzione, ma supporta il comportamento di spostamento tramite tabulazioni standard.

<Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="100"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
               Grid.Row="0"
               FontWeight="ExtraBold"
               HorizontalTextAlignment="Center"
               TextWrapping="Wrap"
               Padding="10" />
    <StackPanel Grid.Row="1"
                Orientation="Horizontal"
                HorizontalAlignment="Center">
        <StackPanel Name="ContainerPrimary"
                    XYFocusKeyboardNavigation="Enabled"
                    KeyDown="ContainerPrimary_KeyDown"
                    Orientation="Horizontal"
                    BorderBrush="Green"
                    BorderThickness="2"
                    Padding="5" Margin="5">
            <Button Name="B1"
                    Content="B1"
                    GettingFocus="Btn_GettingFocus" Margin="5" />
            <Button Name="B2"
                    Content="B2"
                    GettingFocus="Btn_GettingFocus" />
            <StackPanel Name="ContainerSecondary"
                        Orientation="Horizontal"
                        BorderBrush="Red"
                        BorderThickness="2"
                        Margin="5">
                <Button Name="B3"
                        Content="B3"
                        GettingFocus="Btn_GettingFocus"
                        Margin="5" />
                <Button Name="B4"
                        Content="B4"
                        GettingFocus="Btn_GettingFocus"
                        Margin="5" />
            </StackPanel>
        </StackPanel>
        <Button Name="B5"
                Content="B5"
                GettingFocus="Btn_GettingFocus"
                Margin="5" />
    </StackPanel>
</Grid>

È possibile avere più livelli di aree direzionali nidificate. Se tutti gli elementi padre hanno XYFocusKeyboardNavigation impostato su Enabled, i limiti dell'area di spostamento interna vengono ignorati.

Ecco un esempio di due aree direzionali nidificate all'interno di un elemento che non supporta in modo esplicito lo spostamento direzionale 2D. In questo caso, lo spostamento direzionale non è supportato tra le due aree nidificate.

XYFocusKeyboardNavigation enabled and nested behaviorComportamento abilitato e nidificato di XYFocusKeyboardNavigation

Ecco un esempio più complesso di tre aree direzionali nidificate dove:

  • Quando B1 ha lo stato attivo, è possibile spostarsi solo su B5 (e viceversa) perché esiste un limite di area direzionale con XYFocusKeyboardNavigation impostato su Disabled, rendendo B2, B3 e B4 non raggiungibili con i tasti di direzione
  • Quando B2 ha lo stato attivo, è possibile spostarsi solo su B3 (e viceversa) perché il limite dell'area direzionale impedisce lo spostamento con i tasti di direzione su B1, B4 e B5
  • Quando B4 ha lo stato attivo, è necessario usare il tasto TAB per spostarsi tra i controlli

XYFocusKeyboardNavigation enabled and complex nested behavior

Comportamento nidificato complesso e abilitato di XYFocusKeyboardNavigation

Spostamento tramite tabulazioni

Anche se è possibile usare i tasti di direzione per lo spostamento direzionale 2D all'interno di un controllo, o gruppo di controlli, è possibile usare il tasto TAB per spostarsi tra tutti i controlli in un'applicazione Windows.

Tutti i controlli interattivi supportano lo spostamento tramite tabulazione per impostazione predefinita (le proprietà IsEnabled e IsTabStop sono true), con l'ordine di tabulazione logico derivato dal layout del controllo nell'applicazione. Tuttavia, l'ordine predefinito non corrisponde necessariamente all'ordine visivo. La posizione di visualizzazione effettiva può dipendere dal contenitore di layout padre e da determinate proprietà che è possibile impostare sugli elementi figlio per influenzare il layout.

Evitare un ordine di tabulazione personalizzato che provochi uno spostamento casuale dello stato attivo nell'applicazione. Ad esempio, un elenco di controlli in un modulo dovrebbe avere un ordine di tabulazione dall'alto in basso e da sinistra a destra (a seconda delle impostazioni locali).

In questa sezione viene descritto come personalizzare completamente questo ordine di tabulazione in base all'app.

Impostare il comportamento di spostamento tramite tabulazioni

La proprietà TabFocusNavigation di UIElement specifica il comportamento di spostamento tramite tabulazioni per l'intera struttura a oggetti (o area direzionale).

Nota

Usare questa proprietà anziché la proprietà Control.TabNavigation per gli oggetti che non usano un ControlTemplate per definire il relativo aspetto.

Come accennato nella sezione precedente, per evitare un'esperienza di navigazione confusa, è preferibile che gli elementi figlio di un'area direzionale non vengano specificati in modo esplicito nell'ordine di spostamento tramite tabulazioni dell'applicazione. Per altri dettagli sul comportamento di tabulazione per un elemento, vedere le proprietà UIElement.TabFocusNavigation e TabIndex.

Per versioni precedenti a Windows 10 Creators Update (build 10.0.15063), le impostazioni di tabulazione erano limitate agli oggetti ControlTemplate. Per altre informazioni, vedere Control.TabNavigation.

TabFocusNavigation ha un valore di tipo KeyboardNavigationMode con i possibili valori seguenti (si noti che questi esempi non sono gruppi di controlli personalizzati e non richiedono spostamento interno con i tasti di direzione):

  • Local (impostazione predefinita) Gli indici di tabulazione sono riconosciuti nel sottoalbero locale all'interno del contenitore. Per questo esempio, l'ordine di tabulazione è B1, B2, B3, B4, B5, B6, B7, B1.

    Comportamento di spostamento tramite tabulazioni "Local"

  • Once Il contenitore e tutti gli elementi figlio ricevono lo stato attivo una volta. Per questo esempio, l'ordine di tabulazione è B1, B2, B7, B1 (viene anche illustrato lo spostamento interno con tasto di direzione).

    Comportamento di spostamento tramite tabulazioni "Once"

  • Ciclo
    Lo stato attivo torna all'elemento con stato attivabile iniziale all'interno di un contenitore. Per questo esempio, l'ordine di tabulazione è B1, B2, B3, B4, B5, B6, B2...

    Comportamento di spostamento tramite tabulazioni "Cycle"

Ecco il codice per gli esempi precedenti (con TabFocusNavigation = "Cycle").

<Grid 
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" 
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="300"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
               Grid.Row="0" 
               FontWeight="ExtraBold" 
               HorizontalTextAlignment="Center"
               TextWrapping="Wrap" 
               Padding="10" />
    <StackPanel Name="ContainerPrimary"
                KeyDown="Container_KeyDown" 
                Orientation="Horizontal" 
                HorizontalAlignment="Center"
                BorderBrush="Green" 
                BorderThickness="2" 
                Grid.Row="1" 
                Padding="10" 
                MaxWidth="200">
        <Button Name="B1" 
                Content="B1" 
                GettingFocus="Btn_GettingFocus" 
                Margin="5"/>
        <StackPanel Name="ContainerSecondary" 
                    KeyDown="Container_KeyDown"
                    XYFocusKeyboardNavigation="Enabled" 
                    TabFocusNavigation ="Cycle"
                    Orientation="Vertical" 
                    VerticalAlignment="Center"
                    BorderBrush="Red" 
                    BorderThickness="2"
                    Padding="5" Margin="5">
            <Button Name="B2" 
                    Content="B2" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B3" 
                    Content="B3" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B4" 
                    Content="B4" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B5" 
                    Content="B5" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B6" 
                    Content="B6" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
        </StackPanel>
        <Button Name="B7" 
                Content="B7" 
                GettingFocus="Btn_GettingFocus" 
                Margin="5"/>
    </StackPanel>
</Grid>

TabIndex

Usare TabIndex per specificare l'ordine in cui gli elementi ricevono lo stato attivo quando l'utente si sposta tra i controlli usando il tasto TAB. Un controllo con un indice di tabulazione inferiore riceve lo stato attivo prima di un controllo con un indice superiore.

Quando un controllo non ha TabIndex specificato, viene assegnato a esso un valore di indice superiore rispetto al valore di indice massimo corrente (e con la priorità più bassa) di tutti i controlli interattivi nella struttura ad albero visuale, in base all'ambito.

Tutti gli elementi figlio di un controllo sono considerati un ambito e, se uno di questi elementi include anche elementi figlio, vengono considerati un altro ambito. Eventuali ambiguità vengono risolte scegliendo il primo elemento nella struttura ad albero visuale dell'ambito.

Per escludere un controllo dall'ordine di tabulazione, impostare la proprietà IsTabStop su false.

Eseguire l'override dell'ordine di tabulazione predefinito impostando la proprietà TabIndex.

Nota

TabIndex funziona in modo analogo sia con UIElement.TabFocusNavigation sia con Control.TabNavigation.

Qui, si mostra come lo spostamento dello stato attivo può essere condizionato dalla proprietà TabIndex su elementi specifici.

Spostamento tramite tabulazioni "Local" con comportamento TabIndex

Nell'esempio precedente sono presenti due ambiti:

  • B1, area direzionale (B2 - B6) e B7
  • area direzionale (B2 - B6)

Quando B3 (nell'area direzionale) ottiene lo stato attivo, l'ambito cambia e lo spostamento tramite tabulazioni passa all'area direzionale in cui viene identificato il candidato migliore per lo stato attivo successivo. In questo caso, B2 seguito da B4, B5 e B6. L'ambito cambia di nuovo e lo stato attivo passa a B1.

Ecco il codice per questo esempio.

<Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="300"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
               Grid.Row="0"
               FontWeight="ExtraBold"
               HorizontalTextAlignment="Center"
               TextWrapping="Wrap"
               Padding="10" />
    <StackPanel Name="ContainerPrimary"
                KeyDown="Container_KeyDown"
                Orientation="Horizontal"
                HorizontalAlignment="Center"
                BorderBrush="Green"
                BorderThickness="2"
                Grid.Row="1"
                Padding="10"
                MaxWidth="200">
        <Button Name="B1"
                Content="B1"
                TabIndex="1"
                ToolTipService.ToolTip="TabIndex = 1"
                GettingFocus="Btn_GettingFocus"
                Margin="5"/>
        <StackPanel Name="ContainerSecondary"
                    KeyDown="Container_KeyDown"
                    TabFocusNavigation ="Local"
                    Orientation="Vertical"
                    VerticalAlignment="Center"
                    BorderBrush="Red"
                    BorderThickness="2"
                    Padding="5" Margin="5">
            <Button Name="B2"
                    Content="B2"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B3"
                    Content="B3"
                    TabIndex="3"
                    ToolTipService.ToolTip="TabIndex = 3"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B4"
                    Content="B4"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B5"
                    Content="B5"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B6"
                    Content="B6"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
        </StackPanel>
        <Button Name="B7"
                Content="B7"
                TabIndex="2"
                ToolTipService.ToolTip="TabIndex = 2"
                GettingFocus="Btn_GettingFocus"
                Margin="5"/>
    </StackPanel>
</Grid>

Spostamento direzionale 2D per tastiera, game pad e telecomando

Tipi di input non di puntamento, ad esempio tastiera, game pad, telecomando e strumenti di accessibilità come l'Assistente vocale di Windows condividono un meccanismo sottostante comune per spostarsi e interagire con l'interfaccia utente dell'applicazione Windows.

Questa sezione spiega come specificare una strategia di spostamento preferita e ottimizzare lo spostamento dello stato attivo all'interno dell'applicazione tramite un set di proprietà della strategia di spostamento che supportano tutti i tipi di input non di puntamento basati sullo stato attivo.

Per informazioni più generali sulla creazione di app ed esperienze per Xbox/TV, vedere Interazione tramite tastiera, Progettazione per Xbox e TV e Interazioni con game pad e telecomando.

Le strategie di spostamento sono applicabili a tastiera, game pad, telecomando e vari strumenti di accessibilità.

Le proprietà della strategia di spostamento seguenti determinano il controllo che riceve lo stato attivo in base al tasto di direzione, al tasto direzionale (D-pad) o a un tasto simile premuto.

  • XYFocusUpNavigationStrategy
  • XYFocusDownNavigationStrategy
  • XYFocusLeftNavigationStrategy
  • XYFocusRightNavigationStrategy

Queste proprietà hanno i possibili valori Auto (predefinito), NavigationDirectionDistance, Projection o RectilinearDistance .

Se impostato su Auto, il comportamento dell'elemento è basato sui predecessori dell'elemento. Se tutti gli elementi sono impostati su Auto, si usa Projection.

Nota

Altri fattori, ad esempio l'elemento che aveva lo stato attivo in precedenza o la prossimità all'asse della direzione di spostamento, possono influenzare il risultato.

Projection

La strategia di proiezione sposta lo stato attivo sul primo elemento incontrato quando il bordo dell'elemento con lo stato attivo viene proiettato nella direzione di spostamento.

In questo esempio ogni direzione di spostamento dello stato attivo è impostata su Projection. Si noti come lo stato attivo si sposti da B1 a B4, ignorando B3. Questo perché B3 non si trova nella zona di proiezione. Si noti anche come un candidato allo stato attivo non sia identificato durante lo spostamento a sinistra da B1. Questo perché la posizione di B2 rispetto a B1 elimina B3 come candidato. Se B3 si trovasse nella stessa riga di B2, sarebbe un candidato valido per lo spostamento a sinistra. B2 è un candidato valido grazie alla sua prossimità non ostruita all'asse della direzione di spostamento.

Projection navigation strategy

Strategia di spostamento di proiezione

La strategia NavigationDirectionDistance sposta lo stato attivo sull'elemento più vicino all'asse della direzione di spostamento.

Il bordo del rettangolo di delimitazione corrispondente alla direzione di spostamento è esteso e proiettato per identificare le destinazioni candidate. Il primo elemento incontrato viene identificato come destinazione. In caso di più candidati, l'elemento più vicino viene identificato come destinazione. Se sono presenti più candidati, l'elemento più in alto/più a sinistra viene identificato come candidato.

NavigationDirectionDistance navigation strategy

Strategia di spostamento NavigationDirectionDistance

RectilinearDistance

La strategia RectilinearDistance sposta lo stato attivo sull'elemento più vicino in base alla distanza rettilinea 2D (geometria Taxicab).

La somma della distanza primaria e della distanza secondaria per ogni potenziale candidato viene usata per identificare il migliore. In un tie, il primo elemento a sinistra viene selezionato se la direzione richiesta è verso l'alto o verso il basso e il primo elemento in alto viene selezionato se la direzione richiesta è verso sinistra o verso destra.

RectilinearDistance navigation strategy

Strategia di spostamento RectilinearDistance

Questa immagine mostra in che modo, quando B1 ha lo stato attivo e la direzione richiesta è verso il basso, B3 è il candidato dello stato attivo RectilinearDistance. Questo è basato sui calcoli seguenti per questo esempio:

  • Distanza (B1, B3, verso il basso) è 10 + 0 = 10
  • Distanza (B1, B2, verso il basso) è 0 + 40 = 30
  • Distanza (B1, D, verso il basso) è 30 + 0 = 30