Interfaccia utente annidata negli elementi di elenco

L'interfaccia utente annidata è un'interfaccia utente che espone controlli eseguibili annidati racchiusi all'interno di un contenitore che può anche assumere lo stato attivo indipendente.

È possibile usare l'interfaccia utente annidata per presentare a un utente opzioni aggiuntive che consentono di accelerare l'esecuzione di azioni importanti. Tuttavia, più azioni si espongono più complicata diventa l'interfaccia utente. È necessario prestare particolare attenzione quando si sceglie di usare questo modello di interfaccia utente. Questo articolo fornisce linee guida che consentono di determinare il miglior corso di azione per l'interfaccia utente specifica.

API importanti: classe ListView, classe GridView

In questo articolo viene descritta la creazione di un'interfaccia utente annidata negli elementi ListView e GridView . Sebbene questa sezione non parli di altri casi di interfaccia utente annidata, questi concetti sono trasferibili. Prima di iniziare, è necessario avere familiarità con le indicazioni generali per l'uso dei controlli ListView o GridView nell'interfaccia utente, disponibili negli articoli Visualizzazione elenchi e visualizzazione elenco e visualizzazione griglia.

In questo articolo vengono usati i termini elenco, voce di elenco e interfaccia utente annidata, come definito di seguito:

  • L'elenco fa riferimento a una raccolta di elementi contenuti in una visualizzazione elenco o in una visualizzazione griglia.
  • L'elemento elenco fa riferimento a un singolo elemento su cui un utente può intervenire in un elenco.
  • L'interfaccia utente annidata fa riferimento agli elementi dell'interfaccia utente all'interno di una voce di elenco che un utente può intervenire separatamente dall'esecuzione di azioni sull'elemento dell'elenco stesso.

Screenshot showing the parts of a Nested U I.

NOTA: ListView e GridView derivano entrambi dalla classe ListViewBase, pertanto hanno le stesse funzionalità, ma visualizzano i dati in modo diverso. Quando in questo articolo si fa riferimento agli elenchi, le informazioni sono valide per entrambi i controlli ListView e GridView.

Azioni primarie e secondarie.

Quando si crea un'interfaccia utente con un elenco, prendere in considerazione le azioni che l'utente potrebbe eseguire da tali voci di elenco.

  • Un utente può fare clic sull'elemento per eseguire un'azione?
    • In genere, facendo clic su una voce di elenco viene avviata un'azione, ma non è necessario.
  • È possibile eseguire più azioni che l'utente può eseguire?
    • Ad esempio, toccando un messaggio di posta elettronica in un elenco viene aperto il messaggio di posta elettronica. Tuttavia, potrebbero essere presenti altre azioni, ad esempio l'eliminazione del messaggio di posta elettronica, che l'utente vuole eseguire senza aprirlo per primo. L'utente può accedere a questa azione direttamente nell'elenco.
  • Come devono essere esposte le azioni all'utente?
    • Prendere in considerazione tutti i tipi di input. Alcune forme di interfaccia utente annidata funzionano bene con un metodo di input, ma potrebbero non funzionare con altri metodi.

L'azione principale è quella che l'utente si aspetta quando preme l'elemento dell'elenco.

Le azioni secondarie sono in genere acceleratori associati alle voci di elenco. Questi acceleratori possono essere per la gestione degli elenchi o le azioni correlate all'elemento di elenco.

Opzioni per le azioni secondarie

Quando crei l'interfaccia utente di un elenco, devi innanzitutto prendere in considerazione tutti i metodi di input supportati da Windows. Per altre informazioni sui diversi tipi di input, vedi Introduzione all'input.

Dopo aver verificato che la tua app supporti tutti gli input supportati da Windows, stabilisci se le azioni secondarie dell'app sono abbastanza importanti da essere esposte come acceleratori nell'elenco principale. Tenere presente che più azioni si espongono, più complicata diventa l'interfaccia utente. È davvero necessario esporre le azioni secondarie nell'interfaccia utente dell'elenco principale o inserirle in un'altra posizione?

È possibile valutare la possibilità di esporre azioni aggiuntive nell'interfaccia utente dell'elenco principale quando tali azioni devono essere accessibili da qualsiasi input in qualsiasi momento.

Se decidi che l'inserimento di azioni secondarie nell'interfaccia utente dell'elenco principale non è necessario, esistono diversi altri modi per esporli all'utente. Ecco alcune opzioni che è possibile prendere in considerazione per dove inserire le azioni secondarie.

Inserire le azioni secondarie nella pagina dei dettagli

Inserire le azioni secondarie nella pagina a cui si sposta l'elemento di elenco quando viene premuto. Quando si usa il modello elenco/dettagli, la pagina dei dettagli è spesso un buon posto per inserire azioni secondarie.

Per altre info, vedi il modello elenco/dettaglio.

Inserire le azioni secondarie in un menu di scelta rapida

Inserire le azioni secondarie in un menu di scelta rapida a cui l'utente può accedere tramite clic con il pulsante destro del mouse o tenere premuto. Ciò offre il vantaggio di consentire all'utente di eseguire un'azione, ad esempio l'eliminazione di un messaggio di posta elettronica, senza dover caricare la pagina dei dettagli. È consigliabile rendere disponibili anche queste opzioni nella pagina dei dettagli, perché i menu di scelta rapida devono essere acceleratori anziché interfaccia utente primaria.

Per esporre le azioni secondarie quando l'input proviene da un game pad o da un telecomando, è consigliabile usare un menu di scelta rapida.

Per altre info, vedi Menu di scelta rapida e riquadri a comparsa.

Inserisci azioni secondarie nell'interfaccia utente al passaggio del mouse per ottimizzare l'input del puntatore

Se prevedi che l'app venga usata di frequente con input del puntatore, ad esempio mouse e penna, e vuoi rendere le azioni secondarie facilmente disponibili solo per tali input, puoi visualizzare le azioni secondarie solo al passaggio del mouse. Questo acceleratore è visibile solo quando viene usato un input del puntatore, quindi assicurarsi di usare le altre opzioni per supportare anche altri tipi di input.

Nested UI shown on hover

Per altre informazioni, vedere Interazioni con il mouse.

Posizionamento dell'interfaccia utente per azioni primarie e secondarie

Se si decide che le azioni secondarie devono essere esposte nell'interfaccia utente dell'elenco principale, è consigliabile seguire le linee guida seguenti.

Quando si crea una voce di elenco con azioni primarie e secondarie, posizionare l'azione primaria a sinistra e secondaria a destra. Nelle impostazioni cultura di lettura da sinistra a destra gli utenti associano le azioni sul lato sinistro dell'elemento di elenco come azione principale.

In questi esempi si parla dell'interfaccia utente dell'elenco in cui l'elemento scorre più orizzontalmente (è più ampio rispetto all'altezza). Tuttavia, è possibile che siano presenti elementi di elenco più quadrati in forma o più alti rispetto alla larghezza. In genere, si tratta di elementi usati in una griglia. Per questi elementi, se l'elenco non scorre verticalmente, è possibile posizionare le azioni secondarie nella parte inferiore dell'elemento di elenco anziché sul lato destro.

Prendere in considerazione tutti gli input

Quando si decide di usare l'interfaccia utente annidata, valutare anche l'esperienza utente con tutti i tipi di input. Come accennato in precedenza, l'interfaccia utente annidata funziona bene per alcuni tipi di input. Tuttavia, non funziona sempre bene per altri. In particolare, la tastiera, il controller e gli input remoti possono avere difficoltà ad accedere agli elementi dell'interfaccia utente annidati. Rispetta le indicazioni seguenti per garantire il funzionamento di Windows con tutti i tipi di input.

Gestione dell'interfaccia utente annidata

Quando sono annidate più azioni nell'elemento dell'elenco, ti consigliamo di gestire lo spostamento con una tastiera, un game pad, un telecomando o un altro input non puntatore.

Interfaccia utente annidata in cui gli elementi dell'elenco eseguono un'azione

Se l'interfaccia utente dell'elenco con elementi annidati supporta azioni quali richiamo, selezione (singola o multipla) o operazioni di trascinamento della selezione, è consigliabile usare queste tecniche di freccia per spostarsi tra gli elementi dell'interfaccia utente annidati.

Screenshot showing nested U I elements labeled with the letters A, B, C, and D.

Game pad

Quando l'input proviene da un game pad, fornire questa esperienza utente:

  • Da A, il tasto direzionale destro sposta lo stato attivo su B.
  • Da B, il tasto direzionale destro sposta lo stato attivo su C.
  • Da C, il tasto direzionale destro non è operativo o se è presente un elemento dell'interfaccia utente attivabile a destra di List, posizionare lo stato attivo.
  • Da C, il tasto direzionale destro sposta lo stato attivo su B.
  • Da B, il tasto direzionale destro sposta lo stato attivo su A.
  • Da A, il tasto direzionale sinistro non è operativo o se è presente un elemento dell'interfaccia utente attivabile a destra di List, posizionare lo stato attivo.
  • Da A, B o C, verso il basso il tasto direzionale sposta lo stato attivo su D.
  • Dall'elemento dell'interfaccia utente a sinistra dell'elemento elenco, il tasto direzionale destro sposta lo stato attivo su A.
  • Dall'elemento dell'interfaccia utente a destra dell'elemento elenco, il tasto direzionale sinistro sposta lo stato attivo su A.

Tastiera

Quando l'input proviene da una tastiera, si tratta dell'esperienza ottenuta dall'utente:

  • Da A, tabulazione sposta lo stato attivo su B.
  • Da B, tabulazione sposta lo stato attivo su C.
  • Da C, tabulazione sposta lo stato attivo sull'elemento successivo dell'interfaccia utente attivabile nell'ordine di tabulazione.
  • Da C, maiusc+tab sposta lo stato attivo su B.
  • Da B, maiusc+tab o freccia sinistra sposta lo stato attivo su A.
  • Da A, MAIUSC+TAB sposta lo stato attivo sull'elemento successivo dell'interfaccia utente attivabile nell'ordine di tabulazione inverso.
  • Da A, B o C, verso il basso il tasto direzionale sposta lo stato attivo su D.
  • Dall'elemento dell'interfaccia utente a sinistra dell'elemento elenco, il tasto TAB sposta lo stato attivo su A.
  • Dall'elemento dell'interfaccia utente a destra dell'elemento di elenco, maiuscole e tabulazioni sposta lo stato attivo su C.

Per ottenere questa interfaccia utente, impostare IsItemClickEnabled su true nell'elenco. SelectionMode può essere qualsiasi valore.

Per implementare questo codice, vedere la sezione Esempio di questo articolo.

Interfaccia utente annidata in cui gli elementi dell'elenco non eseguono un'azione

È possibile usare una visualizzazione elenco perché fornisce il comportamento di virtualizzazione e scorrimento ottimizzato, ma non è associata un'azione a una voce di elenco. Queste interfacce utente usano in genere l'elemento di elenco solo per raggruppare gli elementi e assicurarsi che scorrono come set.

Questo tipo di interfaccia utente tende a essere molto più complicato degli esempi precedenti, con molti elementi annidati su cui l'utente può intervenire.

Screenshot of a complex Nested U I showing a lot of nested elements that the user can interact with.

Per ottenere questa interfaccia utente, impostare le proprietà seguenti nell'elenco:

<ListView SelectionMode="None" IsItemClickEnabled="False" >
    <ListView.ItemContainerStyle>
         <Style TargetType="ListViewItem">
             <Setter Property="IsFocusEngagementEnabled" Value="True"/>
         </Style>
    </ListView.ItemContainerStyle>
</ListView>

Quando le voci dell'elenco non eseguono un'azione, ti consigliamo di gestire lo spostamento con un game pad o una tastiera.

Game pad

Quando l'input proviene da un game pad, fornire questa esperienza utente:

  • Da Elemento elenco, il tasto direzionale verso il basso sposta lo stato attivo sull'elemento di elenco successivo.
  • Da List Item, il tasto direzionale sinistro/destro non è operativo o se è presente un elemento dell'interfaccia utente attivabile a destra di List, posizionare lo stato attivo.
  • Nell'elemento elenco il pulsante "A" sposta lo stato attivo sull'interfaccia utente annidata nella priorità in alto/in basso a sinistra/destra.
  • Mentre si trova all'interno dell'interfaccia utente annidata, seguire il modello di navigazione XY Focus. Lo stato attivo può spostarsi solo nell'interfaccia utente annidata contenuta all'interno dell'elemento elenco corrente fino a quando l'utente non preme il pulsante "B", che rimette lo stato attivo sull'elemento elenco.

Tastiera

Quando l'input proviene da una tastiera, si tratta dell'esperienza ottenuta dall'utente:

  • Da Elemento elenco, il tasto direzionale verso il basso sposta lo stato attivo sull'elemento di elenco successivo.
  • Da Elemento elenco, premere il tasto sinistro/destro non è operativo.
  • Da Elemento elenco, premendo TAB lo stato attivo viene spostato sulla tabulazione successiva tra l'elemento dell'interfaccia utente annidata.
  • Da uno degli elementi dell'interfaccia utente annidata, premendo tabulazioni gli elementi dell'interfaccia utente annidati vengono attraversati in ordine di tabulazioni. Una volta spostati tutti gli elementi dell'interfaccia utente annidati, lo stato attivo viene spostato sul controllo successivo in ordine di tabulazione dopo ListView.
  • MAIUSC+TAB si comporta in direzione inversa rispetto al comportamento delle schede.

Esempio

Questo esempio illustra come implementare l'interfaccia utente annidata in cui gli elementi dell'elenco eseguono un'azione.

<ListView SelectionMode="None" IsItemClickEnabled="True"
          ChoosingItemContainer="listview1_ChoosingItemContainer"/>
private void OnListViewItemKeyDown(object sender, KeyRoutedEventArgs e)
{
    // Code to handle going in/out of nested UI with gamepad and remote only.
    if (e.Handled == true)
    {
        return;
    }

    var focusedElementAsListViewItem = FocusManager.GetFocusedElement() as ListViewItem;
    if (focusedElementAsListViewItem != null)
    {
        // Focus is on the ListViewItem.
        // Go in with Right arrow.
        Control candidate = null;

        switch (e.OriginalKey)
        {
            case Windows.System.VirtualKey.GamepadDPadRight:
            case Windows.System.VirtualKey.GamepadLeftThumbstickRight:
                var rawPixelsPerViewPixel = DisplayInformation.GetForCurrentView().RawPixelsPerViewPixel;
                GeneralTransform generalTransform = focusedElementAsListViewItem.TransformToVisual(null);
                Point startPoint = generalTransform.TransformPoint(new Point(0, 0));
                Rect hintRect = new Rect(startPoint.X * rawPixelsPerViewPixel, startPoint.Y * rawPixelsPerViewPixel, 1, focusedElementAsListViewItem.ActualHeight * rawPixelsPerViewPixel);
                candidate = FocusManager.FindNextFocusableElement(FocusNavigationDirection.Right, hintRect) as Control;
                break;
        }

        if (candidate != null)
        {
            candidate.Focus(FocusState.Keyboard);
            e.Handled = true;
        }
    }
    else
    {
        // Focus is inside the ListViewItem.
        FocusNavigationDirection direction = FocusNavigationDirection.None;
        switch (e.OriginalKey)
        {
            case Windows.System.VirtualKey.GamepadDPadUp:
            case Windows.System.VirtualKey.GamepadLeftThumbstickUp:
                direction = FocusNavigationDirection.Up;
                break;
            case Windows.System.VirtualKey.GamepadDPadDown:
            case Windows.System.VirtualKey.GamepadLeftThumbstickDown:
                direction = FocusNavigationDirection.Down;
                break;
            case Windows.System.VirtualKey.GamepadDPadLeft:
            case Windows.System.VirtualKey.GamepadLeftThumbstickLeft:
                direction = FocusNavigationDirection.Left;
                break;
            case Windows.System.VirtualKey.GamepadDPadRight:
            case Windows.System.VirtualKey.GamepadLeftThumbstickRight:
                direction = FocusNavigationDirection.Right;
                break;
            default:
                break;
        }

        if (direction != FocusNavigationDirection.None)
        {
            Control candidate = FocusManager.FindNextFocusableElement(direction) as Control;
            if (candidate != null)
            {
                ListViewItem listViewItem = sender as ListViewItem;

                // If the next focusable candidate to the left is outside of ListViewItem,
                // put the focus on ListViewItem.
                if (direction == FocusNavigationDirection.Left &&
                    !listViewItem.IsAncestorOf(candidate))
                {
                    listViewItem.Focus(FocusState.Keyboard);
                }
                else
                {
                    candidate.Focus(FocusState.Keyboard);
                }
            }

            e.Handled = true;
        }
    }
}

private void listview1_ChoosingItemContainer(ListViewBase sender, ChoosingItemContainerEventArgs args)
{
    if (args.ItemContainer == null)
    {
        args.ItemContainer = new ListViewItem();
        args.ItemContainer.KeyDown += OnListViewItemKeyDown;
    }
}
// DependencyObjectExtensions.cs definition.
public static class DependencyObjectExtensions
{
    public static bool IsAncestorOf(this DependencyObject parent, DependencyObject child)
    {
        DependencyObject current = child;
        bool isAncestor = false;

        while (current != null && !isAncestor)
        {
            if (current == parent)
            {
                isAncestor = true;
            }

            current = VisualTreeHelper.GetParent(current);
        }

        return isAncestor;
    }
}