Condividi tramite


NavigationView

Il controllo NavigationView fornisce uno spostamento di primo livello per la tua app. Si adatta a un'ampia gamma di dimensioni dello schermo e supporta entrambi gli stili di spostamento: superiore e a sinistra.

Spostamento superiorespostamento a sinistra
NavigationView supporta il menu o il riquadro di spostamento sia superiore che a sinistra

È il controllo giusto?

NavigationView è un controllo di spostamento adattivo ideale per:

  • Fornire un'esperienza di spostamento coerente in tutta l'app.
  • Risparmiare spazio sullo schermo delle finestre più piccole.
  • Organizzare l'accesso a numerose categorie di spostamento.

Per altri modelli di spostamento, vedi Nozioni di base sulla progettazione della struttura di spostamento.

Piattaforma UWP e WinUI 2

Importante

Le informazioni e gli esempi in questo articolo sono ottimizzati per le app che usano Windows App SDK e WinUI 3, ma sono generalmente applicabili alle app UWP che usano WinUI 2. Per informazioni ed esempi specifici della piattaforma, consultare le indicazioni di riferimento sulle API UWP.

Questa sezione contiene informazioni necessarie per usare il controllo in un'app UWP o WinUI 2.

Il controllo NavigationView per le app UWP è incluso come parte della di WinUI 2. Per maggiori informazioni, incluse le istruzioni per l'installazione, vedere WinUI 2. Le API per questo controllo sono presenti negli spazi dei nomi Windows.UI.Xaml.Controls e lo spazio dei nomi Microsoft.UI.Xaml.Controls.

È consigliabile usare la versione più recente di WinUI 2 per ottenere gli stili, i modelli e le funzionalità più recenti per tutti i controlli. Alcune funzioni di NavigationView, come lo spostamento superiore e gerarchico, richiedono Windows 10, versione 1809 (SDK 17763) o successiva oppure WinUI 2.

Per usare il codice in questo articolo con WinUI 2, usare un alias in XAML (si usa muxc) per rappresentare le API della libreria dell'interfaccia utente di Windows incluse nel progetto. Per altre informazioni, vedere Attività iniziali di WinUI 2.

xmlns:muxc="using:Microsoft.UI.Xaml.Controls"

<muxc:NavigationView />

Creare una visualizzazione di navigazione

L'app Raccolta WinUI 3 include esempi interattivi della maggior parte dei controlli e delle funzionalità di WinUI 3. Scaricare l'app da Microsoft Store od ottenere il codice sorgente su GitHub

Questo esempio mostra come creare di una visualizzazione di navigazione semplice in XAML.

<NavigationView>
    <NavigationView.MenuItems>
        <NavigationViewItem Content="Nav Item A"/>
        <NavigationViewItem Content="Nav Item B"/>
        <NavigationViewItem Content="Nav Item C"/>
    </NavigationView.MenuItems>

    <Frame x:Name="ContentFrame"/>
</NavigationView>

Modalità di visualizzazione

Puoi usare la proprietà PaneDisplayMode per configurare differenti stili di spostamento o modalità di visualizzazione, per NavigationView.

In alto

Il riquadro è posizionato sopra il contenuto.
PaneDisplayMode="Top"

Esempio di spostamento superiore

Ti consigliamo lo spostamento superiore quando:

  • Hai al massimo 5 categorie di spostamento di primo livello ugualmente importanti e ogni ulteriore categoria di spostamento di primo livello che viene inserita nel menu extra a discesa è considerata meno importante.
  • Devi visualizzare tutte le opzioni di spostamento sullo schermo.
  • Vuoi più spazio per il contenuto dell'app.
  • Le icone non possono descrivere in modo chiaro le categorie di spostamento dell'app.

A sinistra

Il riquadro è espanso e posizionato a sinistra del contenuto.
PaneDisplayMode="Left"

Esempio di riquadro di spostamento a sinistra espanso

Ti consigliamo lo spostamento a sinistra quando:

  • Hai 5-10 categorie di spostamento di primo livello altrettanto importanti.
  • Vuoi che le categorie di spostamento siano molto importanti, riservando meno spazio all'altro contenuto dell'app.

LeftCompact

Il riquadro mostra solo le icone fino a quando non viene aperto e posizionato a sinistra del contenuto. Quando viene aperto, il riquadro sovrappone il contenuto.
PaneDisplayMode="LeftCompact"

Esempio di riquadro di spostamento a sinistra compatto

LeftMinimal

Viene visualizzato solo il pulsante del menu finché il riquadro non viene aperto. Quando si apre, il riquadro si sovrappone al lato sinistro del contenuto.
PaneDisplayMode="LeftMinimal"

Esempio di riquadro di spostamento a sinistra minimo

Auto

Per impostazione predefinita, PaneDisplayMode è impostata su Auto. In Auto modalità, NavigationView si adatta tra LeftMinimal quando la finestra è stretta, a LeftCompact e quindi Left quando la finestra diventa più ampia. Per altre informazioni, vedi la sezione sul comportamento adattivo.

Comportamento adattivo predefinito spostamento a sinistra
Comportamento adattivo predefinito NavigationView

Anatomia

Queste immagini mostrano il layout delle aree del riquadro, dell'intestazione e del contenuto del controllo quando la configurazione prevede lo spostamento superiore o a sinistra.

Layout NavigationView superiore
Layout dello spostamento superiore

Layout NavigationView a sinistra
Layout dello spostamento a sinistra

Riquadro

Puoi usare la proprietà PaneDisplayMode per posizionare il riquadro sopra o a sinistra del contenuto.

Il riquadro NavigationView può contenere:

Il riquadro a sinistra contiene anche:

  • Un pulsante di menu per alternare il riquadro aperto e chiuso. Nelle finestre più grandi dell'app quando il riquadro è aperto, puoi scegliere di nascondere questo pulsante usando la proprietà IsPaneToggleButtonVisible.

NavigationView include un pulsante Indietro posizionato nell'angolo in alto a sinistra del riquadro. Tuttavia, non gestisce automaticamente lo spostamento indietro e non aggiunge il contenuto allo stack Indietro. Per abilitare lo spostamento all'indietro, vedi la sezione Spostamento indietro.

Ecco l'anatomia del riquadro dettagliata per le posizioni del riquadro superiore e a sinistra.

Riquadro di spostamento superiore

Anatomia del riquadro superiore NavigationView

  1. Intestazioni
  2. Elementi di spostamento
  3. Separatori
  4. AutoSuggestBox (facoltativo)
  5. Pulsante delle impostazioni (facoltativo)

Riquadro di spostamento sinistro

Anatomia del riquadro a sinistra NavigationView

  1. Pulsante Menu
  2. Elementi di spostamento
  3. Separatori
  4. Intestazioni
  5. AutoSuggestBox (facoltativo)
  6. Pulsante delle impostazioni (facoltativo)

È possibile usare FooterMenuItems per posizionare gli elementi di navigazione alla fine del riquadro di spostamento, a differenza della proprietà MenuItems che inserisce gli elementi all'inizio del riquadro.

Per impostazione predefinita, FooterMenuItems verrà visualizzato prima dell'elemento Settings. L'elemento Settings può comunque essere attivato o disattivato usando la proprietà IsSettingsVisible.

Solo gli elementi di navigazione devono essere posizionati in FooterMenuItems. Tutti gli altri elementi che devono essere allineati al piè di pagina del riquadro possono essere posizionati in PaneFooter.

Per un esempio di come aggiungere FooterMenuItems a NavigationView, vedere la classe FooterMenuItems.

La figura seguente mostra un controllo NavigationView con gli elementi di navigazione Account, Your Cart e Help nel menu a piè di pagina.

Controllo NavigationView con FooterMenuItems

Puoi posizionare contenuto in formato libero nel piè di pagina del riquadro aggiungendolo alla proprietà PaneFooter.

Spostamento superiore piè di pagina riquadro
Piè di pagina riquadro superiore

Spostamento a sinistra piè di pagina riquadro
Piè di pagina del riquadro a sinistra

Intestazione e titolo del riquadro

Puoi posizionare il contenuto del testo nell'area di intestazione del riquadro impostando la proprietà PaneTitle. Accetta una stringa e mostra il testo accanto al pulsante di menu.

Per aggiungere contenuto non testuale, ad esempio un'immagine o un logo, puoi posizionare qualsiasi elemento nell'intestazione del riquadro aggiungendolo alla proprietà PaneHeader.

Se sono impostati sia PaneTitle che PaneHeader, il contenuto viene impilato orizzontalmente accanto al pulsante di menu, con PaneTitle più vicino al pulsante di menu.

Spostamento superiore intestazione riquadro
Intestazione riquadro superiore

Spostamento a sinistra intestazione riquadro
Intestazione del riquadro a sinistra

Contenuto del riquadro

Puoi posizionare contenuto in formato libero nel riquadro aggiungendolo alla proprietà PaneCustomContent.

Spostamento superiore contenuto personalizzato riquadro
Contenuto personalizzato riquadro superiore

Spostamento a sinistra contenuto personalizzato riquadro
Contenuto personalizzato riquadro sinistro

Puoi aggiungere un titolo di pagina impostando la proprietà Header.

Esempio area intestazione NavigationView
L'header NavigationView

L'area dell'intestazione è allineata verticalmente con il pulsante di spostamento nella posizione del riquadro sinistro e si trova sotto il riquadro nella posizione del riquadro superiore. Ha un'altezza fissa pari a 52 pixel. La sua finalità è di contenere il titolo della pagina della categoria di navigazione selezionata. L'intestazione è ancorata alla parte superiore della pagina e funge da punto di ritaglio dello scorrimento per l'area del contenuto.

L'intestazione è visibile ogni volta che NavigationView si trova in modalità Minimal. Puoi scegliere di nascondere l'intestazione nelle altre modalità utilizzate con larghezze di finestra più ampie. Per nascondere l'intestazione, imposta la proprietà AlwaysShowHeader su false.

Contenuto

Esempio area contenuto NavigationView
Contenuto NavigationView

L'area del contenuto è il punto in cui è visualizzata gran parte delle informazioni sulla categoria di navigazione selezionata.

Consigliamo margini di 12 pixel per l'area del contenuto, quando NavigationView è in modalità Minimal e 24 pixel negli altri casi.

Comportamento adattivo

Per impostazione predefinita, NavigationView modifica automaticamente la propria modalità di visualizzazione sulla base dello spazio sullo schermo che ha a disposizione. Le proprietà CompactModeThresholdWidth ed ExpandedModeThresholdWidth specificano i punti di interruzione in corrispondenza di cui cambia la modalità di visualizzazione. Puoi modificare questi valori per personalizzare il comportamento adattivo della modalità di visualizzazione.

Predefiniti

Quando PaneDisplayMode è impostato sul valore predefinito Auto, il comportamento adattivo consiste nel mostrare:

  • Un riquadro a sinistra espanso sulle larghezze di finestre grandi (1008 pixel o superiore).
  • Un riquadro di spostamento a sinistra (LeftCompact), solo a icona, su larghezze di finestre medie (da 641 a 1007 pixel).
  • Solo un pulsante di menu (LeftMinimal) su larghezze di finestre piccole (fino a 640 pixel).

Per altre informazioni sulle dimensioni di finestra per il comportamento adattivo, vedi Dimensioni dello schermo e punti di interruzione.

Comportamento adattivo predefinito spostamento a sinistra
Comportamento adattivo predefinito NavigationView

Minima

Un secondo modello adattivo comune consiste nell'utilizzare un riquadro a sinistra espanso su larghezze di finestre grandi e solo un pulsante di menu su larghezze di finestre piccole e medie.

Ti consigliamo questo approccio quando:

  • Vuoi più spazio per il contenuto dell'app su larghezze di finestre piccole.
  • Le categorie di spostamento non possono essere rappresentate in modo chiaro con icone.

Comportamento adattivo minimo spostamento a sinistra
Comportamento adattivo "minimo" di NavigationView

Per configurare questo comportamento, imposta CompactModeThresholdWidth sulla larghezza in corrispondenza della quale desideri che il riquadro venga compresso. In questo caso viene cambiato dal valore predefinito 640 a 1007. Devi anche impostare ExpandedModeThresholdWidth per assicurare che i valori non siano in conflitto.

<NavigationView CompactModeThresholdWidth="1007" ExpandedModeThresholdWidth="1007"/>

Compact

Un terzo modello adattivo comune è quello di utilizzare un riquadro a sinistra espanso su larghezze di finestre grandi e un riquadro di spostamento LeftCompact, solo a icona, su larghezze di finestre piccole e medie.

Ti consigliamo questo approccio quando:

  • È importante visualizzare sempre tutte le opzioni di spostamento sullo schermo.
  • Le categorie di spostamento possono essere rappresentate in modo chiaro con icone.

Comportamento adattivo compatto spostamento a sinistra
Comportamento adattivo "compatto" di NavigationView

Per configurare questo comportamento, imposta CompactModeThresholdWidth su 0.

<NavigationView CompactModeThresholdWidth="0"/>

Nessun comportamento adattivo

Per disabilitare il comportamento adattivo automatico, imposta PaneDisplayMode su un valore diverso da Auto. Qui è impostato su LeftMinimal, quindi viene mostrato solo il pulsante di menu indipendentemente dalla larghezza della finestra.

Nessun comportamento adattivo spostamento a sinistra
NavigationView con PaneDisplayMode impostata su LeftMinimal

<NavigationView PaneDisplayMode="LeftMinimal" />

Come descritto in precedenza nella sezione Modalità di visualizzazione, puoi impostare il riquadro in modo che sia sempre in alto, sempre espanso, sempre compatto o sempre minimo. Puoi anche gestire autonomamente le modalità di visualizzazione nel codice dell'app. Un esempio è illustrato nella sezione successiva.

Spostamento da superiore a sinistra

Quando si utilizza lo spostamento superiore nell'app, gli elementi di spostamento si comprimono in un menu extra al diminuire della larghezza della finestra. Quando la finestra dell'app è stretta, puoi migliorare l'esperienza utente passando PaneDisplayMode da Top a LeftMinimal, anziché lasciare che tutte le voci vengano compresse nel menu extra.

Ti consigliamo di utilizzare lo spostamento superiore nelle dimensioni di finestre grandi e lo spostamento a sinistra nelle dimensioni di finestre piccole:

  • Hai un gruppo di categorie di spostamento di primo livello altrettanto importanti da visualizzare insieme, in modo tale che se una categoria di questo gruppo non si adatta allo schermo, esegui la compressione passando allo spostamento a sinistra per attribuire la stessa importanza.
  • Vuoi mantenere quanto più spazio possibile per il contenuto nelle finestre piccole.

Questo esempio mostra come usare una proprietà VisualStateManager e AdaptiveTrigger.MinWindowWidth per passare dallo spostamento Top e LeftMinimal.

Esempio di comportamento adattivo superiore o a sinistra 1

<Grid>
    <NavigationView x:Name="NavigationViewControl" >
        <NavigationView.MenuItems>
            <NavigationViewItem Content="A" x:Name="A" />
            <NavigationViewItem Content="B" x:Name="B" />
            <NavigationViewItem Content="C" x:Name="C" />
        </NavigationView.MenuItems>
    </NavigationView>

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState>
                <VisualState.StateTriggers>
                    <AdaptiveTrigger
                        MinWindowWidth="{x:Bind NavigationViewControl.CompactModeThresholdWidth}" />
                </VisualState.StateTriggers>

                <VisualState.Setters>
                    <Setter Target="NavigationViewControl.PaneDisplayMode" Value="Top"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

Suggerimento

Quando utilizzi AdaptiveTrigger.MinWindowWidth, lo stato di visualizzazione si attiva quando la finestra è più ampia della larghezza minima specificata. In altri termini, il valore predefinito XAML definisce la finestra stretta e VisualState definisce le modifiche applicate quando la finestra diventa più ampia. Il valore predefinito di PaneDisplayMode per la visualizzazione di spostamento è Auto; pertanto quando la larghezza della finestra è minore o uguale a CompactModeThresholdWidth, viene usato lo spostamento LeftMinimal. Quando la finestra diventa più ampia, VisualState sostituisce il valore predefinito e viene utilizzato lo spostamento Top.

NavigationView non esegue automaticamente alcuna attività di navigazione. Quando l'utente tocca un elemento di spostamento, NavigationView mostra quell'elemento come selezionato e genera un evento ItemInvoked. Se il tocco genera la selezione di un nuovo elemento, viene generato anche un evento SelectionChanged.

Puoi gestire entrambi gli eventi per eseguire attività relative allo spostamento richiesto. La scelta di quale gestire dipende dal comportamento che desideri per la tua app. In genere, passi alla pagina richiesta e aggiorni l'intestazione della NavigationView in risposta a questi eventi.

  • ItemInvoked viene generato ogni volta che l'utente tocca un elemento di spostamento, anche se è già selezionato. L'elemento può anche essere richiamato con un'azione equivalente usando mouse, tastiera o altro input. Per altre info, vedi Input e interazioni. Se si passa al gestore ItemInvoked, per impostazione predefinita, la pagina verrà ricaricata e viene aggiunta una voce duplicata allo stack di navigazione. Se navighi quando viene richiamato un elemento, devi impedire il ricaricamento della pagina o assicurarti che una voce duplicata non venga creata nel backstack di spostamento quando la pagina viene ricaricata. (Vedi gli esempi di codice).
  • SelectionChanged può essere attivato da un utente che richiama un elemento non selezionato attualmente o cambiando a livello di codice l'elemento selezionato. Se la modifica della selezione avviene perché un utente ha richiamato un elemento, l'evento ItemInvoked si verifica per primo. Se la modifica della selezione è a livello di codice, ItemInvoked non viene attivato.

Tutti gli elementi di navigazione fanno parte dello stesso modello di selezione, indipendentemente dal fatto che appartengano a MenuItems o a FooterMenuItems. È possibile selezionare un solo elemento di navigazione alla volta.

Spostamento indietro

NavigationView include un pulsante Indietro, ma, come per lo spostamento avanti, non esegue automaticamente lo spostamento indietro. Quando l'utente tocca il pulsante Indietro, viene attivato l'evento BackRequested. Puoi gestire questo evento per eseguire lo spostamento indietro. Per altre info ed esempi di codice, vedi Cronologia di spostamento e spostamento indietro.

In Minimal modalità or Compact , NavigationView Pane è aperto come riquadro a comparsa. In questo caso, invece facendo clic sul pulsante Indietro Pane verrà chiuso e verrà generato l'evento PaneClosing.

Puoi nascondere o disabilitare il pulsante Indietro impostando queste proprietà:

  • IsBackButtonVisible: consente di visualizzare e nascondere il pulsante Indietro. Questa proprietà accetta un valore di enumerazione NavigationViewBackButtonVisible ed è impostata su Auto per impostazione predefinita. Quando il pulsante è compresso, non viene riservato alcuno spazio nel layout.
  • IsBackEnabled: consente di abilitare o disabilitare il pulsante Indietro. Puoi eseguire il data binding di questa proprietà alla proprietà CanGoBack del tuo frame di navigazione. BackRequested non viene generato se IsBackEnabled è false.

Pulsante indietro NavigationView nel riquadro di spostamento sinistro
Pulsante Indietro nel riquadro di spostamento sinistro

Pulsante indietro NavigationView nel riquadro di spostamento superiore
Pulsante indietro nel riquadro di spostamento superiore

Esempio di codice

Questo esempio mostra come è possibile utilizzare NavigationView sia con un riquadro di spostamento superiore su finestre grandi che con un riquadro di spostamento a sinistra su finestre piccole. Può essere adattato allo spostamento solo a sinistra rimuovendo le impostazioni di spostamento superiore in VisualStateManager.

L'esempio mostra un modo comune per impostare i dati di spostamento che funzionano per molti scenari. In questo esempio viene innanzitutto archiviato (nel tag di NavigationViewItem) il nome completo del tipo della pagina a cui si desidera accedere. Nel gestore, scomponi quel valore, lo trasformi in Type(C#) o Windows::UI::Xaml::Interop::TypeName(C++/WinRT) e lo usi per passare alla pagina di destinazione. In questo modo è possibile creare unit test per verificare che i valori all'interno dei tag siano di un tipo valido. (Vedi anche Boxing e unboxing dei valori in IInspectable con C++/WinRT). Mostra anche come implementare lo spostamento indietro con il pulsante Indietro di NavigationView.

Questo codice presuppone che l'app contenga pagine con i nomi seguenti per passare a: HomePage, AppsPage, GamesPage, MusicPage, MyContentPage e SettingsPage. Il codice per queste pagine non è mostrato.

<Page ... >
 <Grid>
     <NavigationView x:Name="NavView"
                     Loaded="NavView_Loaded"
                     ItemInvoked="NavView_ItemInvoked"
                     BackRequested="NavView_BackRequested">
         <NavigationView.MenuItems>
             <NavigationViewItem Tag="NavigationViewDemo.HomePage" Icon="Home" Content="Home"/>
             <NavigationViewItemSeparator/>
             <NavigationViewItemHeader x:Name="MainPagesHeader"
                                       Content="Main pages"/>
             <NavigationViewItem Tag="NavigationViewDemo.AppsPage" Content="Apps">
                 <NavigationViewItem.Icon>
                     <FontIcon Glyph="&#xEB3C;"/>
                 </NavigationViewItem.Icon>
             </NavigationViewItem>
             <NavigationViewItem Tag="NavigationViewDemo.GamesPage" Content="Games">
                 <NavigationViewItem.Icon>
                     <FontIcon Glyph="&#xE7FC;"/>
                 </NavigationViewItem.Icon>
             </NavigationViewItem>
             <NavigationViewItem Tag="NavigationViewDemo.MusicPage" Icon="Audio" Content="Music"/>
         </NavigationView.MenuItems>

         <NavigationView.AutoSuggestBox>
             <!-- See AutoSuggestBox documentation for
              more info about how to implement search. -->
             <AutoSuggestBox x:Name="NavViewSearchBox" QueryIcon="Find"/>
         </NavigationView.AutoSuggestBox>

         <ScrollViewer>
             <Frame x:Name="ContentFrame" IsTabStop="True"
                NavigationFailed="ContentFrame_NavigationFailed"/>
         </ScrollViewer>
     </NavigationView>

     <VisualStateManager.VisualStateGroups>
         <VisualStateGroup>
             <VisualState>
                 <VisualState.StateTriggers>
                     <AdaptiveTrigger
                     MinWindowWidth="{x:Bind NavViewCompactModeThresholdWidth}"/>
                 </VisualState.StateTriggers>
                 <VisualState.Setters>
                     <!-- Remove the next 3 lines for left-only navigation. -->
                     <Setter Target="NavView.PaneDisplayMode" Value="Top"/>
                     <Setter Target="NavViewSearchBox.Width" Value="200"/>
                     <Setter Target="MainPagesHeader.Visibility" Value="Collapsed"/>
                     <!-- Leave the next line for left-only navigation. -->
                     <Setter Target="ContentFrame.Padding" Value="24,0,24,24"/>
                 </VisualState.Setters>
             </VisualState>
         </VisualStateGroup>
     </VisualStateManager.VisualStateGroups>
 </Grid>
</Page>
private double NavViewCompactModeThresholdWidth { get { return NavView.CompactModeThresholdWidth; } }

private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
    throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}

private void NavView_Loaded(object sender, RoutedEventArgs e)
{
    // You can also add items in code.
    NavView.MenuItems.Add(new NavigationViewItemSeparator());
    NavView.MenuItems.Add(new NavigationViewItem
    {
        Content = "My content",
        Icon = new SymbolIcon((Symbol)0xF1AD),
        Tag = "NavigationViewDemo.MyContentPage"
    });

    // Add handler for ContentFrame navigation.
    ContentFrame.Navigated += On_Navigated;

    // NavView doesn't load any page by default, so load home page.
    NavView.SelectedItem = NavView.MenuItems[0];
    // If navigation occurs on SelectionChanged, this isn't needed.
    // Because we use ItemInvoked to navigate, we need to call Navigate
    // here to load the home page.
    NavView_Navigate(typeof(HomePage), new EntranceNavigationTransitionInfo());
}

private void NavView_ItemInvoked(NavigationView sender,
                                 NavigationViewItemInvokedEventArgs args)
{
    if (args.IsSettingsInvoked == true)
    {
        NavView_Navigate(typeof(SettingsPage), args.RecommendedNavigationTransitionInfo);
    }
    else if (args.InvokedItemContainer != null)
    {
        Type navPageType = Type.GetType(args.InvokedItemContainer.Tag.ToString());
        NavView_Navigate(navPageType, args.RecommendedNavigationTransitionInfo);
    }
}

// NavView_SelectionChanged is not used in this example, but is shown for completeness.
// You will typically handle either ItemInvoked or SelectionChanged to perform navigation,
// but not both.
private void NavView_SelectionChanged(NavigationView sender,
                                      NavigationViewSelectionChangedEventArgs args)
{
    if (args.IsSettingsSelected == true)
    {
        NavView_Navigate(typeof(SettingsPage), args.RecommendedNavigationTransitionInfo);
    }
    else if (args.SelectedItemContainer != null)
    {
        Type navPageType = Type.GetType(args.SelectedItemContainer.Tag.ToString());
        NavView_Navigate(navPageType, args.RecommendedNavigationTransitionInfo);
    }
}

private void NavView_Navigate(
    Type navPageType,
    NavigationTransitionInfo transitionInfo)
{
    // Get the page type before navigation so you can prevent duplicate
    // entries in the backstack.
    Type preNavPageType = ContentFrame.CurrentSourcePageType;

    // Only navigate if the selected page isn't currently loaded.
    if (navPageType is not null && !Type.Equals(preNavPageType, navPageType))
    {
        ContentFrame.Navigate(navPageType, null, transitionInfo);
    }
}

private void NavView_BackRequested(NavigationView sender,
                                   NavigationViewBackRequestedEventArgs args)
{
    TryGoBack();
}

private bool TryGoBack()
{
    if (!ContentFrame.CanGoBack)
        return false;

    // Don't go back if the nav pane is overlayed.
    if (NavView.IsPaneOpen &&
        (NavView.DisplayMode == NavigationViewDisplayMode.Compact ||
         NavView.DisplayMode == NavigationViewDisplayMode.Minimal))
        return false;

    ContentFrame.GoBack();
    return true;
}

private void On_Navigated(object sender, NavigationEventArgs e)
{
    NavView.IsBackEnabled = ContentFrame.CanGoBack;

    if (ContentFrame.SourcePageType == typeof(SettingsPage))
    {
        // SettingsItem is not part of NavView.MenuItems, and doesn't have a Tag.
        NavView.SelectedItem = (NavigationViewItem)NavView.SettingsItem;
        NavView.Header = "Settings";
    }
    else if (ContentFrame.SourcePageType != null)
    {
        // Select the nav view item that corresponds to the page being navigated to.
        NavView.SelectedItem = NavView.MenuItems
                    .OfType<NavigationViewItem>()
                    .First(i => i.Tag.Equals(ContentFrame.SourcePageType.FullName.ToString()));

        NavView.Header =
            ((NavigationViewItem)NavView.SelectedItem)?.Content?.ToString();

    }
}
// MainPage.idl
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
    ...
    Double NavViewCompactModeThresholdWidth{ get; };
}

// pch.h
...
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Microsoft.UI.Xaml.Media.Animation.h>


// MainPage.h
#pragma once

#include "MainPage.g.h"

namespace muxc
{
    using namespace winrt::Microsoft::UI::Xaml::Controls;
};

namespace winrt::NavigationViewDemo::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        double NavViewCompactModeThresholdWidth();
        void ContentFrame_NavigationFailed(
            Windows::Foundation::IInspectable const& /* sender */,
            Microsoft::UI::Xaml::Navigation::NavigationFailedEventArgs const& args);
        void NavView_Loaded(
            Windows::Foundation::IInspectable const& /* sender */,
            Microsoft::UI::Xaml::RoutedEventArgs const& /* args */);
        void NavView_ItemInvoked(
            Windows::Foundation::IInspectable const& /* sender */,
            muxc::NavigationViewItemInvokedEventArgs const& args);

        // NavView_SelectionChanged is not used in this example, but is shown for completeness.
        // You'll typically handle either ItemInvoked or SelectionChanged to perform navigation,
        // but not both.
        void NavView_SelectionChanged(
            muxc::NavigationView const& /* sender */,
            muxc::NavigationViewSelectionChangedEventArgs const& args);
        void NavView_Navigate(
            Windows::UI::Xaml::Interop::TypeName navPageType,
            Microsoft::UI::Xaml::Media::Animation::NavigationTransitionInfo const& transitionInfo);
        void NavView_BackRequested(
            muxc::NavigationView const& /* sender */,
            muxc::NavigationViewBackRequestedEventArgs const& /* args */);
        void On_Navigated(
            Windows::Foundation::IInspectable const& /* sender */,
            Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& args);
        bool TryGoBack();

    private:

    };
}

namespace winrt::NavigationViewDemo::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}

// MainPage.cpp
#include "pch.h"
#include "MainPage.xaml.h"
#if __has_include("MainPage.g.cpp")
#include "MainPage.g.cpp"
#endif

using namespace winrt;
using namespace Microsoft::UI::Xaml;

namespace winrt::NavigationViewDemo::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }

    double MainPage::NavViewCompactModeThresholdWidth()
    {
        return NavView().CompactModeThresholdWidth();
    }

    void MainPage::ContentFrame_NavigationFailed(
        Windows::Foundation::IInspectable const& /* sender */,
        Microsoft::UI::Xaml::Navigation::NavigationFailedEventArgs const& args)
    {
        throw winrt::hresult_error(
            E_FAIL, winrt::hstring(L"Failed to load Page ") + args.SourcePageType().Name);
    }

    void MainPage::NavView_Loaded(
        Windows::Foundation::IInspectable const& /* sender */,
        Microsoft::UI::Xaml::RoutedEventArgs const& /* args */)
    {
        // You can also add items in code.
        NavView().MenuItems().Append(muxc::NavigationViewItemSeparator());
        muxc::NavigationViewItem navigationViewItem;
        navigationViewItem.Content(winrt::box_value(L"My content"));
        navigationViewItem.Icon(muxc::SymbolIcon(static_cast<muxc::Symbol>(0xF1AD)));
        navigationViewItem.Tag(winrt::box_value(L"NavigationViewDemo.MyContentPage"));
        NavView().MenuItems().Append(navigationViewItem);

        // Add handler for ContentFrame navigation.
        ContentFrame().Navigated({ this, &MainPage::On_Navigated });

        // NavView doesn't load any page by default, so load home page.
        NavView().SelectedItem(NavView().MenuItems().GetAt(0));
        // If navigation occurs on SelectionChanged, then this isn't needed.
        // Because we use ItemInvoked to navigate, we need to call Navigate
        // here to load the home page.
        NavView_Navigate(winrt::xaml_typename<NavigationViewDemo::HomePage>(),
            Microsoft::UI::Xaml::Media::Animation::EntranceNavigationTransitionInfo());
    }

    void MainPage::NavView_ItemInvoked(
        Windows::Foundation::IInspectable const& /* sender */,
        muxc::NavigationViewItemInvokedEventArgs const& args)
    {
        if (args.IsSettingsInvoked())
        {
            NavView_Navigate(winrt::xaml_typename<NavigationViewDemo::SettingsPage>(),
                args.RecommendedNavigationTransitionInfo());
        }
        else if (args.InvokedItemContainer())
        {
            Windows::UI::Xaml::Interop::TypeName pageTypeName;
            pageTypeName.Name = unbox_value<hstring>(args.InvokedItemContainer().Tag());
            pageTypeName.Kind = Windows::UI::Xaml::Interop::TypeKind::Primitive;
            NavView_Navigate(pageTypeName, args.RecommendedNavigationTransitionInfo());
        }
    }

    // NavView_SelectionChanged is not used in this example, but is shown for completeness.
    // You will typically handle either ItemInvoked or SelectionChanged to perform navigation,
    // but not both.
    void MainPage::NavView_SelectionChanged(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewSelectionChangedEventArgs const& args)
    {
        if (args.IsSettingsSelected())
        {
            NavView_Navigate(winrt::xaml_typename<NavigationViewDemo::SettingsPage>(),
                args.RecommendedNavigationTransitionInfo());
        }
        else if (args.SelectedItemContainer())
        {
            Windows::UI::Xaml::Interop::TypeName pageTypeName;
            pageTypeName.Name = unbox_value<hstring>(args.SelectedItemContainer().Tag());
            pageTypeName.Kind = Windows::UI::Xaml::Interop::TypeKind::Primitive;
            NavView_Navigate(pageTypeName, args.RecommendedNavigationTransitionInfo());
        }
    }

    void MainPage::NavView_Navigate(
        Windows::UI::Xaml::Interop::TypeName navPageType,
        Microsoft::UI::Xaml::Media::Animation::NavigationTransitionInfo const& transitionInfo)
    {
        // Get the page type before navigation so you can prevent duplicate
        // entries in the backstack.
        Windows::UI::Xaml::Interop::TypeName preNavPageType =
            ContentFrame().CurrentSourcePageType();

        // Navigate only if the selected page isn't currently loaded.
        if (navPageType.Name != L"" && preNavPageType.Name != navPageType.Name)
        {
            ContentFrame().Navigate(navPageType, nullptr, transitionInfo);
        }
    }

    void MainPage::NavView_BackRequested(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewBackRequestedEventArgs const& /* args */)
    {
        TryGoBack();
    }

    bool MainPage::TryGoBack()
    {
        if (!ContentFrame().CanGoBack())
            return false;
        // Don't go back if the nav pane is overlayed.
        if (NavView().IsPaneOpen() &&
            (NavView().DisplayMode() == muxc::NavigationViewDisplayMode::Compact ||
                NavView().DisplayMode() == muxc::NavigationViewDisplayMode::Minimal))
            return false;
        ContentFrame().GoBack();
        return true;
    }

    void MainPage::On_Navigated(
        Windows::Foundation::IInspectable const& /* sender */,
        Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& args)
    {
        NavView().IsBackEnabled(ContentFrame().CanGoBack());

        if (ContentFrame().SourcePageType().Name ==
            winrt::xaml_typename<NavigationViewDemo::SettingsPage>().Name)
        {
            // SettingsItem is not part of NavView.MenuItems, and doesn't have a Tag.
            NavView().SelectedItem(NavView().SettingsItem().as<muxc::NavigationViewItem>());
            NavView().Header(winrt::box_value(L"Settings"));
        }
        else if (ContentFrame().SourcePageType().Name != L"")
        {
            for (auto&& eachMenuItem : NavView().MenuItems())
            {
                auto navigationViewItem =
                    eachMenuItem.try_as<muxc::NavigationViewItem>();
                {
                    if (navigationViewItem)
                    {
                        winrt::hstring hstringValue =
                            winrt::unbox_value_or<winrt::hstring>(
                                navigationViewItem.Tag(), L"");
                        if (hstringValue == ContentFrame().SourcePageType().Name)
                        {
                            NavView().SelectedItem(navigationViewItem);
                            NavView().Header(navigationViewItem.Content());
                        }
                    }
                }
            }
        }
    }
}

Spostamento gerarchico

Alcune app possono avere una struttura gerarchica più complessa che richiede più di un semplice elenco di elementi di spostamento. Può essere opportuno usare elementi di spostamento di primo livello per visualizzare categorie di pagine ed elementi figlio per visualizzare pagine specifiche. Una struttura di questo tipo è utile anche se sono presenti pagine di tipo hub che si collegano solo ad altre pagine. Per questi casi, è necessario creare un controllo NavigationView di tipo gerarchico.

Per visualizzare un elenco gerarchico di elementi di spostamento annidati nel riquadro, usa la proprietà MenuItems o la proprietà MenuItemsSource di NavigationViewItem. Ogni NavigationViewItem può contenere altri NavigationViewItem ed elementi di carattere organizzativo come le intestazioni e i separatori di elementi. Per visualizzare un elenco gerarchico quando usi MenuItemsSource, imposta ItemTemplate come NavigationViewItem e associa la relativa proprietà MenuItemsSource al livello successivo della gerarchia.

Anche se NavigationViewItem può contenere un numero qualsiasi di livelli annidati, è consigliabile limitare la profondità della gerarchia di spostamento dell'app. Una gerarchia a due livelli è la soluzione ideale per assicurare usabilità e comprensione.

NavigationView mostra le modalità di visualizzazione della gerarchia e Top Left LeftCompact del riquadro. Di seguito è illustrato l'aspetto di un sottoalbero espanso in ognuna delle modalità di visualizzazione del riquadro:

NavigationView con gerarchia

Aggiunta di una gerarchia di elementi nel markup

Questo esempio mostra come dichiarare lo spostamento gerarchico delle app nel markup XAML.

<NavigationView>
    <NavigationView.MenuItems>
        <NavigationViewItem Content="Home" Icon="Home" ToolTipService.ToolTip="Home"/>
        <NavigationViewItem Content="Collections" Icon="Keyboard" ToolTipService.ToolTip="Collections">
            <NavigationViewItem.MenuItems>
                <NavigationViewItem Content="Notes" Icon="Page" ToolTipService.ToolTip="Notes"/>
                <NavigationViewItem Content="Mail" Icon="Mail" ToolTipService.ToolTip="Mail"/>
            </NavigationViewItem.MenuItems>
        </NavigationViewItem>
    </NavigationView.MenuItems>
</NavigationView>

Aggiunta di una gerarchia di elementi tramite data binding

Puoi aggiungere una gerarchia di voci di menu a NavigationView nei modi seguenti:

  • Associando la proprietà MenuItemsSource ai dati gerarchici
  • Definendo il modello di elemento come NavigationViewMenuItem, con il relativo contenuto impostato come etichetta della voce di menu e la relativa proprietà MenuItemsSource associata al livello successivo della gerarchia

Questo esempio illustra anche gli eventi di espansione e compressione. Questi eventi vengono generati per una voce di menu con elementi figlio.

<Page ... >
    <Page.Resources>
        <DataTemplate x:Key="NavigationViewMenuItem" x:DataType="local:Category">
            <NavigationViewItem Content="{x:Bind Name}" MenuItemsSource="{x:Bind Children}"/>
        </DataTemplate>
    </Page.Resources>

    <Grid>
        <NavigationView x:Name="navview"
    MenuItemsSource="{x:Bind Categories, Mode=OneWay}"
    MenuItemTemplate="{StaticResource NavigationViewMenuItem}"
    ItemInvoked="{x:Bind OnItemInvoked}"
    Expanding="OnItemExpanding"
    Collapsed="OnItemCollapsed"
    PaneDisplayMode="Left">
            <StackPanel Margin="10,10,0,0">
                <TextBlock Margin="0,10,0,0" x:Name="ExpandingItemLabel" Text="Last Expanding: N/A"/>
                <TextBlock x:Name="CollapsedItemLabel" Text="Last Collapsed: N/A"/>
            </StackPanel>
        </NavigationView>
    </Grid>
</Page>
public class Category
{
    public String Name { get; set; }
    public String CategoryIcon { get; set; }
    public ObservableCollection<Category> Children { get; set; }
}

public sealed partial class HierarchicalNavigationViewDataBinding : Page
{
    public HierarchicalNavigationViewDataBinding()
    {
        this.InitializeComponent();
    }

    public ObservableCollection<Category> Categories = new ObservableCollection<Category>()
    {
        new Category(){
            Name = "Menu item 1",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 2",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() {
                            Name  = "Menu item 3",
                            CategoryIcon = "Icon",
                            Children = new ObservableCollection<Category>() {
                                new Category() { Name  = "Menu item 4", CategoryIcon = "Icon" },
                                new Category() { Name  = "Menu item 5", CategoryIcon = "Icon" }
                            }
                        }
                    }
                }
            }
        },
        new Category(){
            Name = "Menu item 6",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 7",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() { Name  = "Menu item 8", CategoryIcon = "Icon" },
                        new Category() { Name  = "Menu item 9", CategoryIcon = "Icon" }
                    }
                }
            }
        },
        new Category(){ Name = "Menu item 10", CategoryIcon = "Icon" }
    };

    private void OnItemInvoked(object sender, NavigationViewItemInvokedEventArgs e)
    {
        var clickedItem = e.InvokedItem;
        var clickedItemContainer = e.InvokedItemContainer;
    }
    private void OnItemExpanding(object sender, NavigationViewItemExpandingEventArgs e)
    {
        var nvib = e.ExpandingItemContainer;
        var name = "Last expanding: " + nvib.Content.ToString();
        ExpandingItemLabel.Text = name;
    }
    private void OnItemCollapsed(object sender, NavigationViewItemCollapsedEventArgs e)
    {
        var nvib = e.CollapsedItemContainer;
        var name = "Last collapsed: " + nvib.Content;
        CollapsedItemLabel.Text = name;
    }
}
// Category.idl
namespace HierarchicalNavigationViewDataBinding
{
    runtimeclass Category
    {
        String Name;
        String CategoryIcon;
        Windows.Foundation.Collections.IObservableVector<Category> Children;
    }
}

// Category.h
#pragma once
#include "Category.g.h"

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    struct Category : CategoryT<Category>
    {
        Category();
        Category(winrt::hstring name,
            winrt::hstring categoryIcon,
            Windows::Foundation::Collections::
                IObservableVector<HierarchicalNavigationViewDataBinding::Category> children);

        winrt::hstring Name();
        void Name(winrt::hstring const& value);
        winrt::hstring CategoryIcon();
        void CategoryIcon(winrt::hstring const& value);
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> Children();
        void Children(Windows::Foundation::Collections:
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> const& value);

    private:
        winrt::hstring m_name;
        winrt::hstring m_categoryIcon;
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> m_children;
    };
}

// Category.cpp
#include "pch.h"
#include "Category.h"
#include "Category.g.cpp"

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    Category::Category()
    {
        m_children = winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    }

    Category::Category(
        winrt::hstring name,
        winrt::hstring categoryIcon,
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> children)
    {
        m_name = name;
        m_categoryIcon = categoryIcon;
        m_children = children;
    }

    hstring Category::Name()
    {
        return m_name;
    }

    void Category::Name(hstring const& value)
    {
        m_name = value;
    }

    hstring Category::CategoryIcon()
    {
        return m_categoryIcon;
    }

    void Category::CategoryIcon(hstring const& value)
    {
        m_categoryIcon = value;
    }

    Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
        Category::Children()
    {
        return m_children;
    }

    void Category::Children(
        Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
            const& value)
    {
        m_children = value;
    }
}

// MainPage.idl
import "Category.idl";

namespace HierarchicalNavigationViewDataBinding
{
    [default_interface]
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        Windows.Foundation.Collections.IObservableVector<Category> Categories{ get; };
    }
}

// MainPage.h
#pragma once

#include "MainPage.g.h"

namespace muxc
{
    using namespace winrt::Microsoft::UI::Xaml::Controls;
};

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
            Categories();

        void OnItemInvoked(muxc::NavigationView const& sender, muxc::NavigationViewItemInvokedEventArgs const& args);
        void OnItemExpanding(
            muxc::NavigationView const& sender,
            muxc::NavigationViewItemExpandingEventArgs const& args);
        void OnItemCollapsed(
            muxc::NavigationView const& sender,
            muxc::NavigationViewItemCollapsedEventArgs const& args);

    private:
        Windows::Foundation::Collections::
            IObservableVector<HierarchicalNavigationViewDataBinding::Category> m_categories;
    };
}

namespace winrt::HierarchicalNavigationViewDataBinding::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}

// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

#include "Category.h"

namespace winrt::HierarchicalNavigationViewDataBinding::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();

        m_categories =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();

        auto menuItem10 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 10", L"Icon", nullptr);

        auto menuItem9 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 9", L"Icon", nullptr);
        auto menuItem8 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 8", L"Icon", nullptr);
        auto menuItem7Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem7Children.Append(*menuItem9);
        menuItem7Children.Append(*menuItem8);

        auto menuItem7 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 7", L"Icon", menuItem7Children);
        auto menuItem6Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem6Children.Append(*menuItem7);

        auto menuItem6 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 6", L"Icon", menuItem6Children);

        auto menuItem5 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 5", L"Icon", nullptr);
        auto menuItem4 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 4", L"Icon", nullptr);
        auto menuItem3Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem3Children.Append(*menuItem5);
        menuItem3Children.Append(*menuItem4);

        auto menuItem3 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 3", L"Icon", menuItem3Children);
        auto menuItem2Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem2Children.Append(*menuItem3);

        auto menuItem2 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 2", L"Icon", menuItem2Children);
        auto menuItem1Children =
            winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
        menuItem1Children.Append(*menuItem2);

        auto menuItem1 = winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
            (L"Menu item 1", L"Icon", menuItem1Children);

        m_categories.Append(*menuItem1);
        m_categories.Append(*menuItem6);
        m_categories.Append(*menuItem10);
    }

    Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category>
        MainPage::Categories()
    {
        return m_categories;
    }

    void MainPage::OnItemInvoked(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewItemInvokedEventArgs const& args)
    {
        auto clickedItem = args.InvokedItem();
        auto clickedItemContainer = args.InvokedItemContainer();
    }

    void MainPage::OnItemExpanding(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewItemExpandingEventArgs const& args)
    {
        auto nvib = args.ExpandingItemContainer();
        auto name = L"Last expanding: " + winrt::unbox_value<winrt::hstring>(nvib.Content());
        ExpandingItemLabel().Text(name);
    }

    void MainPage::OnItemCollapsed(
        muxc::NavigationView const& /* sender */,
        muxc::NavigationViewItemCollapsedEventArgs const& args)
    {
        auto nvib = args.CollapsedItemContainer();
        auto name = L"Last collapsed: " + winrt::unbox_value<winrt::hstring>(nvib.Content());
        CollapsedItemLabel().Text(name);
    }
}

Selezione

Per impostazione predefinita, qualsiasi elemento può contenere elementi figlio, essere richiamato o selezionato.

Quando si fornisce agli utenti una struttura gerarchica di opzioni di spostamento, è possibile scegliere di rendere non selezionabili gli elementi padre, ad esempio quando nell'app non è associata una pagina di destinazione a tali elementi. Se gli elementi padre sono selezionabili, è consigliabile usare la modalità di visualizzazione del riquadro Top o Left espansa. In modalità LeftCompact l'utente deve passare all'elemento padre per aprire il sottoalbero figlio ogni volta che viene richiamato.

Gli indicatori di selezione degli elementi vengono visualizzati lungo il bordo sinistro quando il riquadro è in modalità Left o lungo il bordo inferiore quando invece è in modalità Top. Di seguito sono riportati esempi di NavigationView in modalità Left e Top con un elemento padre selezionato.

NavigationView in modalità Left con elemento padre selezionato

NavigationView in modalità Top con elemento padre selezionato

È possibile che non sempre l'elemento selezionato rimanga visibile. Se è selezionato un elemento figlio in un sottoalbero compresso o non espanso, verrà visualizzato come selezionato il primo predecessore visibile. L'indicatore di selezione verrà spostato di nuovo sull'elemento selezionato se/quando il sottoalbero verrà espanso.

Nell'immagine precedente, ad esempio, l'utente potrebbe selezionare l'elemento del calendario e quindi comprimere il relativo sottoalbero. In questo caso, l'indicatore di selezione verrebbe visualizzato sotto l'elemento Account poiché Account è il primo predecessore visibile del calendario. L'indicatore di selezione passerà di nuovo all'elemento del calendario quando l'utente espanderà il sottoalbero.

L'intero controllo NavigationView visualizzerà un solo indicatore di selezione.

In entrambe le modalità Top e Left, quando fai clic sulle frecce di NavigationViewItem, il sottoalbero viene espanso o compresso. Quando fai clic o tocchi un punto diverso da NavigationViewItem, viene attivato l'evento ItemInvoked e anche in questo caso il sottoalbero viene compresso o espanso.

Per impedire a un elemento di mostrare l'indicatore di selezione quando viene richiamato, imposta su False la relativa proprietà SelectsOnInvoked, come illustrato di seguito:

<Page ...>
    <Page.Resources>
        <DataTemplate x:Key="NavigationViewMenuItem" x:DataType="local:Category">
            <NavigationViewItem Content="{x:Bind Name}"
            MenuItemsSource="{x:Bind Children}"
            SelectsOnInvoked="{x:Bind IsLeaf}"/>
        </DataTemplate>
    </Page.Resources>

    <Grid>
        <NavigationView x:Name="navview"
    MenuItemsSource="{x:Bind Categories, Mode=OneWay}"
    MenuItemTemplate="{StaticResource NavigationViewMenuItem}">
        </NavigationView>
    </Grid>
</Page>
public class Category
{
    public String Name { get; set; }
    public String CategoryIcon { get; set; }
    public ObservableCollection<Category> Children { get; set; }
    public bool IsLeaf { get; set; }
}

public sealed partial class HierarchicalNavigationViewDataBinding : Page
{
    public HierarchicalNavigationViewDataBinding()
    {
        this.InitializeComponent();
    }

    public ObservableCollection<Category> Categories = new ObservableCollection<Category>()
    {
        new Category(){
            Name = "Menu item 1",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 2",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() {
                            Name  = "Menu item 3",
                            CategoryIcon = "Icon",
                            Children = new ObservableCollection<Category>() {
                                new Category() { Name  = "Menu item 4", CategoryIcon = "Icon", IsLeaf = true },
                                new Category() { Name  = "Menu item 5", CategoryIcon = "Icon", IsLeaf = true }
                            }
                        }
                    }
                }
            }
        },
        new Category(){
            Name = "Menu item 6",
            CategoryIcon = "Icon",
            Children = new ObservableCollection<Category>() {
                new Category(){
                    Name = "Menu item 7",
                    CategoryIcon = "Icon",
                    Children = new ObservableCollection<Category>() {
                        new Category() { Name  = "Menu item 8", CategoryIcon = "Icon", IsLeaf = true },
                        new Category() { Name  = "Menu item 9", CategoryIcon = "Icon", IsLeaf = true }
                    }
                }
            }
        },
        new Category(){ Name = "Menu item 10", CategoryIcon = "Icon", IsLeaf = true }
    };
}
// Category.idl
namespace HierarchicalNavigationViewDataBinding
{
    runtimeclass Category
    {
        ...
        Boolean IsLeaf;
    }
}

// Category.h
...
struct Category : CategoryT<Category>
{
    ...
    Category(winrt::hstring name,
        winrt::hstring categoryIcon,
        Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category> children,
        bool isleaf = false);
    ...
    bool IsLeaf();
    void IsLeaf(bool value);

private:
    ...
    bool m_isleaf;
};

// Category.cpp
...
Category::Category(winrt::hstring name,
    winrt::hstring categoryIcon,
    Windows::Foundation::Collections::IObservableVector<HierarchicalNavigationViewDataBinding::Category> children,
    bool isleaf) : m_name(name), m_categoryIcon(categoryIcon), m_children(children), m_isleaf(isleaf) {}
...
bool Category::IsLeaf()
{
    return m_isleaf;
}

void Category::IsLeaf(bool value)
{
    m_isleaf = value;
}

// MainPage.h and MainPage.cpp
// Delete OnItemInvoked, OnItemExpanding, and OnItemCollapsed.

// MainPage.cpp
...
MainPage::MainPage()
{
    InitializeComponent();

    m_categories = winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();

    auto menuItem10 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 10", L"Icon", nullptr, true);

    auto menuItem9 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 9", L"Icon", nullptr, true);
    auto menuItem8 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 8", L"Icon", nullptr, true);
    auto menuItem7Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem7Children.Append(*menuItem9);
    menuItem7Children.Append(*menuItem8);

    auto menuItem7 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 7", L"Icon", menuItem7Children);
    auto menuItem6Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem6Children.Append(*menuItem7);

    auto menuItem6 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 6", L"Icon", menuItem6Children);

    auto menuItem5 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 5", L"Icon", nullptr, true);
    auto menuItem4 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 4", L"Icon", nullptr, true);
    auto menuItem3Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem3Children.Append(*menuItem5);
    menuItem3Children.Append(*menuItem4);

    auto menuItem3 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 3", L"Icon", menuItem3Children);
    auto menuItem2Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem2Children.Append(*menuItem3);

    auto menuItem2 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 2", L"Icon", menuItem2Children);
    auto menuItem1Children =
        winrt::single_threaded_observable_vector<HierarchicalNavigationViewDataBinding::Category>();
    menuItem1Children.Append(*menuItem2);

    auto menuItem1 =
        winrt::make_self<HierarchicalNavigationViewDataBinding::implementation::Category>
        (L"Menu item 1", L"Icon", menuItem1Children);

    m_categories.Append(*menuItem1);
    m_categories.Append(*menuItem6);
    m_categories.Append(*menuItem10);
}
...

Utilizzo dei tasti nel controllo NavigationView di tipo gerarchico

Gli utenti possono spostare lo stato attivo nella NavigationView utilizzando la tastiera. I tasti di direzione espongono lo "spostamento interno" all'interno del riquadro e seguono le interazioni fornite nella visualizzazione ad albero. Le azioni dei tasti cambiano quando ti sposti nella struttura di NavigationView o nel relativo menu a comparsa, che viene visualizzato nelle modalità Top e LeftCompact di HierarchicalNavigationView. Di seguito sono riportate le azioni specifiche che ogni tasto può eseguire in un controllo NavigationView di tipo gerarchico:

Chiave In modalità Left In modalità Top Nel riquadro a comparsa
Attivo Sposta lo stato attivo sull'elemento immediatamente superiore a quello con stato attivo. Non esegue operazioni. Sposta lo stato attivo sull'elemento immediatamente superiore a quello con stato attivo.
Giù Sposta lo stato attivo sull'elemento immediatamente inferiore a quello con stato attivo.* Non esegue operazioni. Sposta lo stato attivo sull'elemento immediatamente inferiore a quello con stato attivo.*
A destra Non esegue operazioni. Sposta lo stato attivo sull'elemento immediatamente a destra di quello con stato attivo. Non esegue operazioni.
A sinistra Non esegue operazioni. Sposta lo stato attivo sull'elemento immediatamente a sinistra di quello con stato attivo. Non esegue operazioni.
BARRA SPAZIATRICE/INVIO Se sono presenti elementi figlio, espande o comprime l'elemento e non cambia lo stato attivo. Se sono presenti elementi figlio, espande gli elementi figlio in un riquadro a comparsa e posiziona lo stato attivo sul primo elemento nel riquadro. Richiama o seleziona l'elemento e chiude il riquadro a comparsa.
ESC Non esegue operazioni. Non esegue operazioni. Chiude il riquadro a comparsa.

La BARRA SPAZIATRICE o il tasto INVIO richiama sempre o seleziona un elemento.

*Non è necessario che gli elementi siano visivamente adiacenti. Lo stato attivo passa dall'ultimo elemento nell'elenco del riquadro all'elemento delle impostazioni.

Sfondi del riquadro

Per impostazione predefinita, il riquadro NavigationView usa uno sfondo diverso a seconda della modalità di visualizzazione:

  • il riquadro è di colore grigio uniforme quando viene espanso a sinistra, fianco a fianco con il contenuto (in modalità Left).
  • il riquadro utilizza l'acrilico in-app quando è aperto come sovrimpressione sul contenuto (in modalità superiore, minima o compatta).

Per modificare lo sfondo del riquadro, puoi sovrascrivere le risorse del tema XAML usate per eseguire il rendering dello sfondo in ogni modalità. Questa tecnica viene usata al posto di una singola proprietà PaneBackground per supportare sfondi diversi per modalità di visualizzazione differenti.

Questa tabella mostra quale risorsa del tema viene utilizzata in ogni modalità di visualizzazione.

Display mode Risorsa del tema
A sinistra NavigationViewExpandedPaneBackground
LeftCompact
LeftMinimal
NavigationViewDefaultPaneBackground
In alto NavigationViewTopPaneBackground

Questo esempio mostra come sovrascrivere le risorse del tema in App.xaml. Quando sostituisci le risorse del tema, dovresti sempre fornire almeno i dizionari delle risorse "Default" e "HighContrast" e i dizionari per le risorse "Light" o "Dark" secondo le necessità. Per ulteriori informazioni, vedi ResourceDictionary.ThemeDictionaries.

Importante

Questo codice mostra come utilizzare la versione di WinUI 2 di AcrylicBrush. Se invece usi la versione della piattaforma di AcrylicBrush, la versione minima per il tuo progetto app deve essere SDK 16299 o successiva. Per usare la versione della piattaforma, rimuovi tutti i riferimenti a muxm:.

<Application ... xmlns:muxm="using:Microsoft.UI.Xaml.Media" ...>
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"/>
                <ResourceDictionary>
                    <ResourceDictionary.ThemeDictionaries>
                        <ResourceDictionary x:Key="Default">
                            <!-- The "Default" theme dictionary is used unless a specific
                                 light, dark, or high contrast dictionary is provided. These
                                 resources should be tested with both the light and dark themes,
                                 and specific light or dark resources provided as needed. -->
                            <muxm:AcrylicBrush x:Key="NavigationViewDefaultPaneBackground"
                                   BackgroundSource="Backdrop"
                                   TintColor="LightSlateGray"
                                   TintOpacity=".6"/>
                            <muxm:AcrylicBrush x:Key="NavigationViewTopPaneBackground"
                                   BackgroundSource="Backdrop"
                                   TintColor="{ThemeResource SystemAccentColor}"
                                   TintOpacity=".6"/>
                            <LinearGradientBrush x:Key="NavigationViewExpandedPaneBackground"
                                     StartPoint="0.5,0" EndPoint="0.5,1">
                                <GradientStop Color="LightSlateGray" Offset="0.0" />
                                <GradientStop Color="White" Offset="1.0" />
                            </LinearGradientBrush>
                        </ResourceDictionary>
                        <ResourceDictionary x:Key="HighContrast">
                            <!-- Always include a "HighContrast" dictionary when you override
                                 theme resources. This empty dictionary ensures that the
                                 default high contrast resources are used when the user
                                 turns on high contrast mode. -->
                        </ResourceDictionary>
                    </ResourceDictionary.ThemeDictionaries>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Spazi vuoti superiori

La proprietà IsTitleBarAutoPaddingEnabled richiede WinUI 2.2 o versione successiva.

In alcuni casi puoi scegliere di personalizzare la barra del titolo della finestra, estendendo potenzialmente il contenuto dell'app nell'area della barra del titolo. Quando NavigationView è l'elemento radice nelle app che si estendono nella barra del titolo usando l'API ExtendViewIntoTitleBar, il controllo regola automaticamente la posizione degli elementi interattivi per impedire la sovrapposizione con l'area trascinabile.

Un'app che si estende nella barra del titolo

Se l'app specifica l'area trascinabile chiamando il metodo Window.SetTitleBar e preferisci che i pulsanti Indietro e Menu siano posizionati più vicino alla parte superiore della finestra dell'app, imposta IsTitleBarAutoPaddingEnabled su false.

App che si estende nella barra del titolo senza spazi aggiuntivi

<muxc:NavigationView x:Name="NavView" IsTitleBarAutoPaddingEnabled="False">

Osservazioni:

Per regolare ulteriormente la posizione dell'area di intestazione di NavigationView, esegui l'override della risorsa del tema XAML NavigationViewHeaderMargin, ad esempio nelle risorse della pagina.

<Page.Resources>
    <Thickness x:Key="NavigationViewHeaderMargin">12,0</Thickness>
</Page.Resources>

Questa risorsa del tema modifica il margine intorno a NavigationView.Header.