Spostamento dello stato attivo a livello di codiceProgrammatic focus navigation

Tastiera, remote e D-Pad

Per spostare lo stato attivo a livello di codice nell'applicazione Windows, è possibile usare il metodo FocusManager. TryMoveFocus o il metodo FindNextElement .To move focus programmatically in your Windows application, you can use either the FocusManager.TryMoveFocus method or the FindNextElement method.

TryMoveFocus tenta di modificare lo stato attivo dall'elemento con lo stato attivo al successivo elemento attivabile nella direzione specificata, mentre FindNextElement recupera l'elemento (come DependencyObject) che riceve lo stato attivo in base alla direzione di navigazione specificata (solo navigazione direzionale, non può essere usato per emulare la navigazione tramite tabulazione).TryMoveFocus attempts to change focus from the element with focus to the next focusable element in the specified direction, while FindNextElement retrieves the element (as a DependencyObject) that will receive focus based on the specified navigation direction (directional navigation only, cannot be used to emulate tab navigation).

Nota

Si consiglia di utilizzare il metodo FindNextElement invece di FindNextFocusableElement perché FindNextFocusableElement recupera un oggetto UIElement, che restituisce null se il successivo elemento attivabile non è un UIElement, ad esempio un oggetto Hyperlink.We recommend using the FindNextElement method instead of FindNextFocusableElement because FindNextFocusableElement retrieves a UIElement, which returns null if the next focusable element is not a UIElement (such as a Hyperlink object).

Trovare un candidato per lo stato attivo all'interno di un ambitoFind a focus candidate within a scope

È possibile personalizzare il comportamento di spostamento dello stato attivo sia per TryMoveFocus che per FindNextElement, inclusa la ricerca del candidato attivo successivo all'interno di una struttura ad albero dell'interfaccia utente specifica oppure escludendo gli elementi specifici dalla considerazione.You can customize the focus navigation behavior for both TryMoveFocus and FindNextElement, including searching for the next focus candidate within a specific UI tree or excluding specific elements from consideration.

Questo esempio usa un gioco TicTacToe per illustrare i metodi TryMoveFocus e FindNextElement .This example uses a TicTacToe game to demonstrate the TryMoveFocus and FindNextElement methods.

<StackPanel Orientation="Horizontal"
                VerticalAlignment="Center"
                HorizontalAlignment="Center" >
    <Button Content="Start Game" />
    <Button Content="Undo Movement" />
    <Grid x:Name="TicTacToeGrid" KeyDown="OnKeyDown">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="50" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="50" />
            <RowDefinition Height="50" />
        </Grid.RowDefinitions>
        <myControls:TicTacToeCell 
            Grid.Column="0" Grid.Row="0" 
            x:Name="Cell00" />
        <myControls:TicTacToeCell 
            Grid.Column="1" Grid.Row="0" 
            x:Name="Cell10"/>
        <myControls:TicTacToeCell 
            Grid.Column="2" Grid.Row="0" 
            x:Name="Cell20"/>
        <myControls:TicTacToeCell 
            Grid.Column="0" Grid.Row="1" 
            x:Name="Cell01"/>
        <myControls:TicTacToeCell 
            Grid.Column="1" Grid.Row="1" 
            x:Name="Cell11"/>
        <myControls:TicTacToeCell 
            Grid.Column="2" Grid.Row="1" 
            x:Name="Cell21"/>
        <myControls:TicTacToeCell 
            Grid.Column="0" Grid.Row="2" 
            x:Name="Cell02"/>
        <myControls:TicTacToeCell 
            Grid.Column="1" Grid.Row="2" 
            x:Name="Cell22"/>
        <myControls:TicTacToeCell 
            Grid.Column="2" Grid.Row="2" 
            x:Name="Cell32"/>
    </Grid>
</StackPanel>
private void OnKeyDown(object sender, KeyRoutedEventArgs e)
{
    DependencyObject candidate = null;

    var options = new FindNextElementOptions ()
    {
        SearchRoot = TicTacToeGrid,
        XYFocusNavigationStrategyOverride = XYFocusNavigationStrategyOverride.Projection
    };

    switch (e.Key)
    {
        case Windows.System.VirtualKey.Up:
            candidate = 
                FocusManager.FindNextElement(
                    FocusNavigationDirection.Up, options);
            break;
        case Windows.System.VirtualKey.Down:
            candidate = 
                FocusManager.FindNextElement(
                    FocusNavigationDirection.Down, options);
            break;
        case Windows.System.VirtualKey.Left:
            candidate = FocusManager.FindNextElement(
                FocusNavigationDirection.Left, options);
            break;
        case Windows.System.VirtualKey.Right:
            candidate = 
                FocusManager.FindNextElement(
                    FocusNavigationDirection.Right, options);
            break;
    }
    // Also consider whether candidate is a Hyperlink, WebView, or TextBlock.
    if (candidate != null && candidate is Control)
    {
        (candidate as Control).Focus(FocusState.Keyboard);
    }
}

Usare FindNextElementOptions per personalizzare ulteriormente il modo in cui vengono identificati i candidati dello stato attivo.Use FindNextElementOptions to further customize how focus candidates are identified. Questo oggetto fornisce le proprietà seguenti:This object provides the following properties:

  • SearchRoot : definire l'ambito della ricerca per gli elementi figlio di questo DependencyObject.SearchRoot - Scope the search for focus navigation candidates to the children of this DependencyObject. Null indica di avviare la ricerca dalla radice della struttura ad albero visuale.Null indicates to start the search from the root of the visual tree.

Importante

Se una o più trasformazioni vengono applicate ai discendenti di SearchRoot che le inseriscono al di fuori dell'area direzionale, questi elementi vengono comunque considerati candidati.If one or more transforms are applied to the descendants of SearchRoot that place them outside of the directional area, these elements are still considered candidates.

  • ExclusionRect : i candidati di spostamento dello stato attivo vengono identificati usando un rettangolo di delimitazione "fittizio" in cui tutti gli oggetti sovrapposti vengono esclusi dallo stato attivo.ExclusionRect - Focus navigation candidates are identified using a "fictitious" bounding rectangle where all overlapping objects are excluded from navigation focus. Questo rettangolo viene utilizzato solo per i calcoli e non viene mai aggiunto alla struttura ad albero visuale.This rectangle is used only for calculations and is never added to the visual tree.
  • HintRect : i candidati di spostamento dello stato attivo vengono identificati usando un rettangolo di delimitazione "fittizio" che identifica gli elementi che con maggiore probabilità ricevono lo stato attivo.HintRect - Focus navigation candidates are identified using a "fictitious" bounding rectangle that identifies the elements most likely to receive focus. Questo rettangolo viene utilizzato solo per i calcoli e non viene mai aggiunto alla struttura ad albero visuale.This rectangle is used only for calculations and is never added to the visual tree.
  • XYFocusNavigationStrategyOverride : strategia di spostamento dello stato attivo usata per identificare l'elemento candidato migliore per ricevere lo stato attivo.XYFocusNavigationStrategyOverride - The focus navigation strategy used to identify the best candidate element to receive focus.

Nell'immagine seguente vengono illustrati alcuni di questi concetti.The following image illustrates some of these concepts.

Quando l'elemento B ha lo stato attivo, FindNextElement identifica I come candidato per lo stato attivo quando si sposta verso destra.When element B has focus, FindNextElement identifies I as the focus candidate when navigating to the right. per questi motivi:The reasons for this are:

  • A causa di HintRect su un oggetto, il riferimento iniziale è a, non BBecause of the HintRect on A, the starting reference is A, not B
  • C non è un candidato perché il pannello è stato specificato come SearchRootC is not a candidate because MyPanel has been specified as the SearchRoot
  • F non è un candidato perché ExclusionRect lo sovrapponeF is not a candidate because the ExclusionRect overlaps it

Comportamento di esplorazione dello stato attivo personalizzato con hint di navigazione

Comportamento di esplorazione dello stato attivo personalizzato con hint di navigazioneCustom focus navigation behavior using navigation hints

Evento NoFocusCandidateFoundNoFocusCandidateFound event

L'evento UIElement. NoFocusCandidateFound viene generato quando si preme la scheda o i tasti di direzione e non è presente alcun candidato nello stato attivo nella direzione specificata.The UIElement.NoFocusCandidateFound event is fired when the tab or arrow keys are pressed and there is no focus candidate in the specified direction. Questo evento non viene generato per TryMoveFocus.This event is not fired for TryMoveFocus.

Poiché si tratta di un evento indirizzato, le bolle dall'elemento con lo stato attivo fino agli oggetti padre successivi fino alla radice dell'albero degli oggetti.Because this is a routed event, it bubbles from the focused element up through successive parent objects to the root of the object tree. Ciò consente di gestire l'evento laddove appropriato.This lets you handle the event wherever appropriate.

In questo esempio viene illustrato come una griglia apre un SplitView quando l'utente tenta di spostare lo stato attivo a sinistra del controllo con lo stato attivo più a sinistra (vedere progettazione per Xbox e TV).Here, we show how a Grid opens a SplitView when the user attempts to move focus to the left of the left-most focusable control (see Designing for Xbox and TV).

<Grid NoFocusCandidateFound="OnNoFocusCandidateFound">
...
</Grid>
private void OnNoFocusCandidateFound (
    UIElement sender, NoFocusCandidateFoundEventArgs args)
{
    if(args.NavigationDirection == FocusNavigationDirection.Left)
    {
        if(args.InputDevice == FocusInputDeviceKind.Keyboard ||
        args.InputDevice == FocusInputDeviceKind.GameController )
            {
                OpenSplitPaneView();
            }
        args.Handled = true;
    }
}

Eventi GotFocus e LostFocusGotFocus and LostFocus events

Gli eventi UIElement. GotFocus e UIElement. LostFocus vengono generati quando un elemento riceve lo stato attivo o perde lo stato attivo, rispettivamente.The UIElement.GotFocus and UIElement.LostFocus events are fired when an element gets focus or loses focus, respectively. Questo evento non viene generato per TryMoveFocus.This event is not fired for TryMoveFocus.

Poiché si tratta di eventi indirizzati, questi vengono bollati dall'elemento con lo stato attivo fino agli oggetti padre successivi alla radice dell'albero degli oggetti.Because these are routed events, they bubble from the focused element up through successive parent objects to the root of the object tree. Ciò consente di gestire l'evento laddove appropriato.This lets you handle the event wherever appropriate.

Eventi GettingFocus e LosingFocusGettingFocus and LosingFocus events

Gli eventi UIElement. GettingFocus e UIElement. LosingFocus vengono generati prima dei rispettivi eventi UIElement. GotFocus e UIElement. LostFocus .The UIElement.GettingFocus and UIElement.LosingFocus events fire before the respective UIElement.GotFocus and UIElement.LostFocus events.

Poiché si tratta di eventi indirizzati, questi vengono bollati dall'elemento con lo stato attivo fino agli oggetti padre successivi alla radice dell'albero degli oggetti.Because these are routed events, they bubble from the focused element up through successive parent objects to the root of the object tree. Poiché ciò si verifica prima che venga eseguita una modifica dello stato attivo, è possibile reindirizzare o annullare la modifica dello stato attivo.As this happens before a focus change takes place, you can redirect or cancel the focus change.

GettingFocus e LosingFocus sono eventi sincroni, quindi lo stato attivo non verrà spostato mentre questi eventi sono di bubbling.GettingFocus and LosingFocus are synchronous events so focus won’t be moved while these events are bubbling. Tuttavia, GotFocus e LostFocus sono eventi asincroni, il che significa che non vi è alcuna garanzia che lo stato attivo non venga spostato di nuovo prima dell'esecuzione del gestore.However, GotFocus and LostFocus are asynchronous events, which means there is no guarantee that focus won’t move again before the handler is executed.

Se lo stato attivo viene spostato attraverso una chiamata a Control. Focus, GettingFocus viene generato durante la chiamata, mentre GotFocus viene generato dopo la chiamata.If focus moves through a call to Control.Focus, GettingFocus is raised during the call, while GotFocus is raised after the call.

La destinazione di spostamento dello stato attivo può essere modificata durante gli eventi GettingFocus e LosingFocus (prima dello spostamento dello stato attivo) tramite la proprietà GettingFocusEventArgs. NewFocusedElement .The focus navigation target can be changed during the GettingFocus and LosingFocus events (before focus moves) through the GettingFocusEventArgs.NewFocusedElement property. Anche se la destinazione viene modificata, l'evento viene ancora bollato e la destinazione può essere modificata nuovamente.Even if the target is changed, the event still bubbles and the target can be changed again.

Per evitare problemi di ricorrenza, viene generata un'eccezione se si tenta di spostare lo stato attivo (tramite TryMoveFocus o Control. Focus) mentre questi eventi sono di bubbling.To avoid reentrancy issues, an exception is thrown if you try to move focus (using TryMoveFocus or Control.Focus) while these events are bubbling.

Questi eventi vengono attivati indipendentemente dal motivo dello spostamento dello stato attivo (inclusa la navigazione tra le schede, la navigazione direzionale e la navigazione a livello di codice).These events are fired regardless of the reason for the focus moving (including tab navigation, directional navigation, and programmatic navigation).

Di seguito è riportato l'ordine di esecuzione degli eventi di attivazione:Here is the order of execution for the focus events:

  1. LosingFocus Se lo stato attivo viene reimpostato nuovamente sull'elemento attivo perso o TryCancel ha esito positivo, non viene generato alcun evento.LosingFocus If focus is reset back to the losing focus element or TryCancel is successful, no further events are fired.
  2. GettingFocus Se lo stato attivo viene reimpostato nuovamente sull'elemento attivo perso o TryCancel ha esito positivo, non viene generato alcun evento.GettingFocus If focus is reset back to the losing focus element or TryCancel is successful, no further events are fired.
  3. LostFocusLostFocus
  4. GotFocusGotFocus

Nell'immagine seguente viene illustrato come, quando si esegue il passaggio a destra da A, XYFocus sceglie B4 come candidato.The following image shows how, when moving to the right from A, the XYFocus chooses B4 as a candidate. B4 genera quindi l'evento GettingFocus in cui ListView ha la possibilità di riassegnare lo stato attivo a B3.B4 then fires the GettingFocus event where the ListView has the opportunity to reassign focus to B3.

Modifica della destinazione di spostamento dello stato attivo nell'evento GettingFocus

Modifica della destinazione di spostamento dello stato attivo nell'evento GettingFocusChanging focus navigation target on GettingFocus event

In questo esempio viene illustrato come gestire l'evento GettingFocus e reindirizzare lo stato attivo.Here, we show how to handle the GettingFocus event and redirect focus.

<StackPanel Orientation="Horizontal">
    <Button Content="A" />
    <ListView x:Name="MyListView" SelectedIndex="2" GettingFocus="OnGettingFocus">
        <ListViewItem>LV1</ListViewItem>
        <ListViewItem>LV2</ListViewItem>
        <ListViewItem>LV3</ListViewItem>
        <ListViewItem>LV4</ListViewItem>
        <ListViewItem>LV5</ListViewItem>
    </ListView>
</StackPanel>
private void OnGettingFocus(UIElement sender, GettingFocusEventArgs args)
{
    //Redirect the focus only when the focus comes from outside of the ListView.
    // move the focus to the selected item
    if (MyListView.SelectedIndex != -1 && 
        IsNotAChildOf(MyListView, args.OldFocusedElement))
    {
        var selectedContainer = 
            MyListView.ContainerFromItem(MyListView.SelectedItem);
        if (args.FocusState == 
            FocusState.Keyboard && 
            args.NewFocusedElement != selectedContainer)
        {
            args.TryRedirect(
                MyListView.ContainerFromItem(MyListView.SelectedItem));
            args.Handled = true;
        }
    }
}

In questo esempio viene illustrato come gestire l'evento LosingFocus per una CommandBar e impostare lo stato attivo quando il menu è chiuso.Here, we show how to handle the LosingFocus event for a CommandBar and set focus when the menu is closed.

<CommandBar x:Name="MyCommandBar" LosingFocus="OnLosingFocus">
     <AppBarButton Icon="Back" Label="Back" />
     <AppBarButton Icon="Stop" Label="Stop" />
     <AppBarButton Icon="Play" Label="Play" />
     <AppBarButton Icon="Forward" Label="Forward" />

     <CommandBar.SecondaryCommands>
         <AppBarButton Icon="Like" Label="Like" />
         <AppBarButton Icon="Share" Label="Share" />
     </CommandBar.SecondaryCommands>
 </CommandBar>
private void OnLosingFocus(UIElement sender, LosingFocusEventArgs args)
{
    if (MyCommandBar.IsOpen == true && 
        IsNotAChildOf(MyCommandBar, args.NewFocusedElement))
    {
        if (args.TryCancel())
        {
            args.Handled = true;
        }
    }
}

Trova il primo e l'ultimo elemento attivabileFind the first and last focusable element

I metodi FocusManager. FindFirstFocusableElement e FocusManager. FindLastFocusableElement spostano lo stato attivo al primo o all'ultimo elemento attivabile all'interno dell'ambito di un oggetto (struttura ad albero dell'elemento di un UIElement o della struttura di testo di un elemento TextElement).The FocusManager.FindFirstFocusableElement and FocusManager.FindLastFocusableElement methods move focus to the first or last focusable element within the scope of an object (the element tree of a UIElement or the text tree of a TextElement). L'ambito viene specificato nella chiamata. se l'argomento è null, l'ambito è la finestra corrente.The scope is specified in the call (if the argument is null, the scope is the current window).

Se non è possibile identificare alcun candidato nello stato attivo nell'ambito, viene restituito null.If no focus candidates can be identified in the scope, null is returned.

In questo esempio viene illustrato come specificare che i pulsanti di una barra dei comandi hanno un comportamento direzionale a capo (vedere interazioni tra tastiera).Here, we show how to specify that the buttons of a CommandBar have a wrap-around directional behavior (see Keyboard Interactions).

<CommandBar x:Name="MyCommandBar" LosingFocus="OnLosingFocus">
    <AppBarButton Icon="Back" Label="Back" />
    <AppBarButton Icon="Stop" Label="Stop" />
    <AppBarButton Icon="Play" Label="Play" />
    <AppBarButton Icon="Forward" Label="Forward" />

    <CommandBar.SecondaryCommands>
        <AppBarButton Icon="Like" Label="Like" />
        <AppBarButton Icon="ReShare" Label="Share" />
    </CommandBar.SecondaryCommands>
</CommandBar>
private void OnLosingFocus(UIElement sender, LosingFocusEventArgs args)
{
    if (IsNotAChildOf(MyCommandBar, args.NewFocussedElement))
    {
        DependencyObject candidate = null;
        if (args.Direction == FocusNavigationDirection.Left)
        {
            candidate = FocusManager.FindLastFocusableElement(MyCommandBar);
        }
        else if (args.Direction == FocusNavigationDirection.Right)
        {
            candidate = FocusManager.FindFirstFocusableElement(MyCommandBar);
        }
        if (candidate != null)
        {
            args.NewFocusedElement = candidate;
            args.Handled = true;
        }
    }
}