Navigazione della Shell MAUI di .NET

Sfoglia l'esempio. Sfoglia l'esempio

.NET Multipiattaforma App UI (.NET MAUI) Shell include un'esperienza di spostamento basata su URI che usa le route per passare a qualsiasi pagina nell'app, senza dover seguire una gerarchia di navigazione impostata. Inoltre, offre anche la possibilità di spostarsi all'indietro senza dover visitare tutte le pagine nello stack di navigazione.

La Shell classe definisce le proprietà correlate allo spostamento seguenti:

  • BackButtonBehavior, di tipo BackButtonBehavior, una proprietà associata che definisce il comportamento del pulsante Indietro.
  • CurrentItem, di tipo ShellItem, l'elemento attualmente selezionato.
  • CurrentPage, di tipo Page, la pagina attualmente presentata.
  • CurrentState, di tipo ShellNavigationState, lo stato di navigazione corrente dell'oggetto Shell.
  • Current, di tipo Shell, che fornisce accesso alla shell corrente.

Le BackButtonBehaviorproprietà , CurrentIteme CurrentState sono supportate da BindableProperty oggetti , il che significa che queste proprietà possono essere destinazioni di data binding.

Lo spostamento viene eseguito richiamando il GoToAsync metodo dalla Shell classe . Quando la navigazione sta per essere eseguita, viene generato l'evento Navigating, e l'evento Navigated viene generato al termine della navigazione.

Note

Lo spostamento può comunque essere eseguito tra le pagine di un'app Shell usando la Navigation proprietà . Per altre informazioni, vedere Eseguire la navigazione senza modalità.

Percorsi

Lo spostamento viene eseguito in un'app Shell specificando un URI a cui passare. Gli URI di spostamento possono avere tre componenti:

  • Route, che definisce il percorso verso il contenuto che fa parte della gerarchia visiva della Shell.
  • Una pagina. Le pagine che non esistono nella gerarchia visiva della Shell possono essere inserite nello stack di navigazione da qualsiasi punto nel contesto di un'app Shell. Ad esempio, una pagina dei dettagli non verrà definita nella gerarchia visiva della shell, ma può essere inserita nello stack di navigazione in base alle esigenze.
  • Uno o più parametri di query. I parametri di query sono parametri che possono essere passati alla pagina di destinazione durante lo spostamento.

Quando un URI di spostamento include tutti e tre i componenti, la struttura è: //route/page?queryParameters

Registrare le route

Le rotte possono essere definite su oggetti FlyoutItem, TabBar, Tab e ShellContent tramite le relative proprietà Route.

<Shell ...>
    <FlyoutItem ...
                Route="animals">
        <Tab ...
             Route="domestic">
            <ShellContent ...
                          Route="cats" />
            <ShellContent ...
                          Route="dogs" />
        </Tab>
        <ShellContent ...
                      Route="monkeys" />
        <ShellContent ...
                      Route="elephants" />  
        <ShellContent ...
                      Route="bears" />
    </FlyoutItem>
    <ShellContent ...
                  Route="about" />                  
    ...
</Shell>

Note

A tutti gli elementi della gerarchia della shell è associata una route. Se non si imposta una route, ne viene generata una in fase di esecuzione. Tuttavia, le route generate non sono sicuramente coerenti tra diverse sessioni dell'app.

L'esempio precedente crea la seguente gerarchia di percorsi, che può essere usata nella navigazione programmata.

animals
  domestic
    cats
    dogs
  monkeys
  elephants
  bears
about

Per navigare all'oggetto per la route ShellContent, l'URI di route assoluto per dogs è //animals/domestic/dogs. Analogamente, per navigare all'oggetto ShellContent relativo al percorso about, l'URI assoluto del percorso è //about.

Avviso

Verrà generata un'eccezione ArgumentException all'avvio dell'app se viene rilevata una rotta duplicata. Questa eccezione verrà lanciata anche se due o più percorsi allo stesso livello nella gerarchia condividono un nome di percorso.

Registrare le rotte della pagina dettagli

Shell Nel costruttore della sottoclasse o in qualsiasi altra posizione eseguita prima che venga richiamata una route, è possibile registrare esplicitamente route aggiuntive per le pagine di dettaglio che non sono rappresentate nella gerarchia visiva dello Shell. Questa operazione viene eseguita con il Routing.RegisterRoute metodo :

Routing.RegisterRoute("monkeydetails", typeof(MonkeyDetailPage));
Routing.RegisterRoute("beardetails", typeof(BearDetailPage));
Routing.RegisterRoute("catdetails", typeof(CatDetailPage));
Routing.RegisterRoute("dogdetails", typeof(DogDetailPage));
Routing.RegisterRoute("elephantdetails", typeof(ElephantDetailPage));

In questo esempio vengono registrate le pagine di dettaglio, non definite nella Shell sottoclasse, come route. È quindi possibile passare a queste pagine di dettaglio usando lo spostamento basato su URI, da qualsiasi posizione all'interno dell'app. Le route per tali pagine sono note come route globali.

Avviso

Verrà generata un'eccezione ArgumentException se la funzione Routing.RegisterRoute tenta di registrare la stessa rotta a due o più tipi diversi.

In alternativa, le pagine possono essere registrate in gerarchie di route diverse, se necessario:

Routing.RegisterRoute("monkeys/details", typeof(MonkeyDetailPage));
Routing.RegisterRoute("bears/details", typeof(BearDetailPage));
Routing.RegisterRoute("cats/details", typeof(CatDetailPage));
Routing.RegisterRoute("dogs/details", typeof(DogDetailPage));
Routing.RegisterRoute("elephants/details", typeof(ElephantDetailPage));

In questo esempio viene abilitata la navigazione contestuale delle pagine, dove navigando alla route details dalla pagina per la route monkeys viene visualizzato il MonkeyDetailPage. Analogamente, passando al percorso details dalla pagina per il percorso elephants viene visualizzato il ElephantDetailPage. Per altre informazioni, vedere Navigazione contestuale.

Note

Le pagine le cui route sono state registrate con il Routing.RegisterRoute metodo possono essere annullate con il Routing.UnRegisterRoute metodo , se necessario.

Modelli di route

A partire da .NET 11, una route registrata con il Routing.RegisterRoute metodo può includere parametri di percorso. Una route che contiene uno o più parametri di percorso è nota come modello di route. I parametri di percorso acquisiscono parte dell'URI di spostamento e lo recapitano alla pagina di destinazione come dati di navigazione, eliminando la necessità di codificare ogni valore come stringa di query:

Routing.RegisterRoute("trip/{tripId}", typeof(TripDetailPage));

Con questa registrazione, passando a //routes/trip/SEA-204, dove routes è una route nella gerarchia visiva della shell, visualizza TripDetailPage e lo distribuisce SEA-204 come parametro di navigazione denominato tripId.

I modelli di route sono una funzionalità aggiuntiva. Le route che non contengono un parametro di percorso non sono interessate e non è necessaria alcuna nuova API per usarle.

Note

Per un esempio che esegue ogni modulo di modello di route supportato, vedere l'esempio di modelli di route della shell.

Sintassi del modello di route

Un parametro path viene scritto come nome racchiuso tra parentesi graffe e occupa un singolo segmento della route, a meno che non sia un parametro catch-all. La tabella seguente illustra i moduli supportati:

Form Descrizione Route di esempio URI di esempio
{name} Parametro obbligatorio che corrisponde esattamente a un segmento. trip/{tripId} //routes/trip/SEA-204
{name?} Parametro facoltativo che corrisponde a zero o a un segmento. traveler/{name?} //routes/traveler/Ada o //routes/traveler
{name=value} Parametro il cui valore predefinito viene recapitato quando il segmento è assente. Un valore predefinito implica che il parametro è facoltativo. rating/{stars=5} //routes/rating/4 o //routes/rating
{name:constraint} Parametro che corrisponde solo quando il segmento soddisfa il vincolo. reservation/{reservationId:int} //routes/reservation/42
{*name} Parametro catch-all che acquisisce tutti i segmenti rimanenti come valore singolo, separato da /. files/{*path} //routes/files/trips/SEA-204/receipt.pdf
prefix{name}suffix Segmento misto, in cui un parametro viene confrontato tra il testo letterale all'interno di un singolo segmento. trip-{tripId}-summary //routes/trip-SEA-204-summary

I moduli possono essere combinati. Ad esempio, {reservationId:int?} dichiara un parametro facoltativo che deve essere un numero intero quando viene fornito e {stars:int=5} dichiara un parametro che deve essere un numero intero e che per impostazione predefinita è 5.

Importante

Un modello di route viene convalidato quando viene registrato. Se il modello non è valido, il Routing.RegisterRoute metodo genera un ArgumentException oggetto che descrive il problema. Per altre informazioni, vedere Restrizioni del modello di route.

Vincoli dei parametri di route

Un vincolo limita i valori corrispondenti a un parametro path e viene aggiunto al nome del parametro con due punti. Sono supportati i vincoli seguenti:

Constraint Descrizione Route di esempio Corrispondenze Non corrisponde
int Un valore integer a 32 bit. reservation/{reservationId:int} 42 abc
long Un valore integer a 64 bit. loyalty/{points:long} 9000000000 abc
double Numero, analizzato usando le impostazioni cultura invarianti. budget/{amount:double} 1299.50 abc
bool true o false, in qualsiasi combinazione di maiuscole e minuscole. toggle/{enabled:bool} true 1
guid Un GUID. booking/{reference:guid} 550e8400-e29b-41d4-a716-446655440000 12345
alpha Una o più lettere. region/{name:alpha} Pacific Pacific2

Non vengono riconosciuti altri nomi di vincolo e un parametro può avere al massimo un vincolo. La registrazione di una route con un vincolo non riconosciuto genera un'eccezione ArgumentException.

Un vincolo controlla solo se una route corrisponde a un URI di navigazione. Non converte il valore acquisito, che viene sempre recapitato alla pagina di destinazione come .string

Note

Quando un URI non soddisfa un vincolo, la route semplicemente non corrisponde. Se nessun'altra route registrata corrisponde all'URI, il GoToAsync metodo genera un'eccezione ArgumentException, nello stesso modo in cui si passa a qualsiasi route sconosciuta.

Registrare i modelli di route

I modelli di route vengono registrati con il Routing.RegisterRoute metodo , allo stesso modo delle route letterali:

Routing.RegisterRoute("trip/{tripId}", typeof(RequiredResultPage));
Routing.RegisterRoute("traveler/{name?}", typeof(TravelerResultPage));
Routing.RegisterRoute("rating/{stars=5}", typeof(DefaultValueResultPage));
Routing.RegisterRoute("reservation/{reservationId:int}", typeof(IntConstraintResultPage));
Routing.RegisterRoute("booking/{reference:guid}", typeof(GuidConstraintResultPage));
Routing.RegisterRoute("files/{*path}", typeof(CatchAllResultPage));
Routing.RegisterRoute("trip-{tripId}-summary", typeof(MixedResultPage));

Le pagine le cui route sono state registrate come modelli di route possono essere annullate con il Routing.UnRegisterRoute metodo , se necessario.

La navigazione in un modello di route viene eseguita con il GoToAsync metodo specificando un URI assoluto. Come per qualsiasi route globale, l'URI inizia con una route definita nella gerarchia visiva shell, seguita dal modello di route, i cui segmenti forniscono i valori per i parametri del percorso:

await Shell.Current.GoToAsync("//routes/trip/SEA-204");

In questo esempio, routes è la route di un ShellContent oggetto nella gerarchia visiva shell e il tripId parametro path del trip/{tripId} modello acquisisce il valore SEA-204.

Importante

I modelli di route funzionano solo con lo spostamento assoluto. Il tentativo di passare a un modello di route con un URI relativo, ad esempio await Shell.Current.GoToAsync("trip/SEA-204"), genera un'eccezione ArgumentException.

Al termine della navigazione, la Shell.Current.CurrentState.Location proprietà contiene l'URI risolto, che include i valori acquisiti anziché i token del modello.

Valori dei parametri del percorso di ricezione

I valori dei parametri di percorso vengono recapitati alla pagina di destinazione usando gli stessi meccanismi dei parametri di query, quindi non è necessaria alcuna API aggiuntiva. La classe che rappresenta la pagina a cui si passa oppure la classe per la pagina può BindingContextessere decorata con un QueryPropertyAttribute oggetto per il parametro :

[QueryProperty(nameof(TripId), "tripId")]
public partial class TripDetailPage : ContentPage
{
    string tripId;

    public string TripId
    {
        get => tripId;
        set
        {
            tripId = value;
            OnPropertyChanged();
        }
    }

    public TripDetailPage()
    {
        InitializeComponent();
        BindingContext = this;
    }
}

In alternativa, la classe ricevente può implementare l'interfaccia IQueryAttributable , in cui ogni parametro di percorso viene visualizzato nel query dizionario:

public class TripDetailViewModel : IQueryAttributable
{
    public string TripId { get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.TryGetValue("tripId", out object tripId))
            TripId = tripId.ToString();
    }
}

Le regole seguenti si applicano ai valori recapitati:

  • La chiave è il nome del parametro, senza parentesi graffe e senza alcun vincolo, marcatore facoltativo o valore predefinito. Ad esempio, la chiave per {reservationId:int} è reservationId.
  • Il valore è sempre un oggetto string, indipendentemente da qualsiasi vincolo applicato al parametro .
  • I valori dei parametri di percorso sono decodificati in URL prima che vengano recapitati, indipendentemente dal fatto che vengano ricevuti tramite QueryPropertyAttribute o IQueryAttributable. Ad esempio, passando a recapita SEA 204.//routes/trip/SEA%20204 Qualsiasi vincolo viene valutato rispetto al valore decodificato. Ciò differisce dai valori della stringa di query, che vengono decodificati automaticamente solo quando vengono ricevuti tramite QueryPropertyAttribute.
  • Un parametro facoltativo assente dall'URI e senza valore predefinito non viene recapitato.
  • Anche un parametro dichiarato da un modello di route precedente nell'URI viene recapitato alle pagine di cui viene eseguito il push in un secondo momento nella stessa navigazione.

Precedenza di corrispondenza della route

Quando più route registrate possono corrispondere a un URI di navigazione o quando viene specificato più volte un valore, si applicano le regole di precedenza seguenti:

  • Una route letterale ha la precedenza su un modello di route. Ad esempio, se e trip/{tripId}trip/summary sono registrati, passare a //routes/trip/summary visualizza la pagina registrata per trip/summary.
  • Un parametro path ha la precedenza su un parametro di query con lo stesso nome. Ad esempio, passando a recapita SEA-204.//routes/trip/SEA-204?tripId=ignored

Restrizioni del modello di route

Il Routing.RegisterRoute metodo genera un'eccezione ArgumentException quando un modello di route viola una delle regole seguenti:

Rule Esempio non valido
Un parametro deve avere un nome e il nome deve essere un identificatore C# valido. trip/{}
Un nome di parametro può essere visualizzato una sola volta in una route. trip/{id}/{id}
Un parametro catch-all deve essere l'ultimo segmento di una route. files/{*path}/details
Un parametro facoltativo o un parametro con un valore predefinito deve essere l'ultimo segmento di una route. trip/{tripId?}/summary
Un vincolo deve essere uno di int, longdouble, bool, , guido alpha. trip/{tripId:decimal}
Un valore predefinito deve soddisfare il vincolo applicato al relativo parametro. reservation/{id:int=abc}
Un segmento misto può contenere solo un parametro e le parentesi graffe devono essere ben formate. trip-{tripId}-{leg}

Inoltre, si applicano le limitazioni seguenti:

  • Un modello di route non può essere costituito solo da un parametro di percorso, ad esempio {category}, perché tale route non può essere trovata senza ambiguità. Passando a una route registrata in questo modo, viene generata un'eccezione ArgumentException.
  • Un segmento misto supporta un vincolo, ma non supporta i moduli facoltativi, predefiniti o catch-all.

Eseguire la navigazione

Per eseguire la navigazione, è necessario prima ottenere un riferimento alla Shell sottoclasse. Questo riferimento può essere ottenuto tramite la Shell.Current proprietà . La navigazione può quindi essere eseguita chiamando il GoToAsync metodo sull'oggetto Shell . Questo metodo naviga verso ShellNavigationState e restituisce Task che verrà completato quando l'animazione di navigazione sarà completata. L'oggetto ShellNavigationState viene costruito dal metodo GoToAsync, da un string o un Uri, e la proprietà Location è impostata sul valore dell'argomento string o Uri.

Importante

Quando si passa a una route dalla gerarchia visiva della Shell, uno stack di navigazione non viene creato. Tuttavia, quando si passa a una pagina che non si trova nella gerarchia visiva della shell, viene creato uno stack di navigazione.

Importante

Sempre await il GoToAsync metodo . Il modello di navigazione fire-and-forget provoca race condition: il codice che viene eseguito dopo la chiamata potrebbe completarsi prima del completamento della navigazione, causando parametri di query mancanti, un errore di CurrentPage o fallimenti di navigazione invisibili all'utente.

// Correct: always await GoToAsync
await Shell.Current.GoToAsync($"details?id={item.Id}");

// Incorrect: fire-and-forget causes a race condition
Shell.Current.GoToAsync($"details?id={item.Id}");

Lo stato di navigazione corrente dell'oggetto Shell può essere recuperato tramite la Shell.Current.CurrentState proprietà , che include l'URI della route visualizzata nella Location proprietà .

Percorsi assoluti

La navigazione può essere eseguita specificando un URI assoluto valido come argomento per il GoToAsync metodo :

await Shell.Current.GoToAsync("//animals/monkeys");

In questo esempio si accede alla pagina per la monkeys route, con la route definita su un ShellContent oggetto. L'oggetto ShellContent che rappresenta la monkeys route è figlio di un FlyoutItem oggetto , la cui route è animals.

Avviso

I percorsi assoluti non funzionano con le pagine registrate tramite il metodo Routing.RegisterRoute.

Importante

Un URI assoluto non può essere rooted direttamente in una pagina registrata con il Routing.RegisterRoute metodo . Tuttavia, può iniziare con una route nella gerarchia visiva della shell e continuare in un modello di route registrato. Per altre informazioni, vedere Modelli di route.

Route relative

È anche possibile eseguire lo spostamento specificando un URI relativo valido come argomento per il GoToAsync metodo . Il sistema di routing tenterà di associare l'URI a un ShellContent oggetto . Pertanto, se tutte le route in un'app sono univoche, la navigazione può essere eseguita specificando solo il nome di route univoco come URI relativo.

Nell'esempio seguente viene visualizzata la pagina per la monkeydetails route:

await Shell.Current.GoToAsync("monkeydetails");

In questo esempio viene eseguita la ricerca della route monkeyDetails fino a quando non viene trovata la pagina corrispondente. Quando la pagina viene trovata, viene aggiunta allo stack di navigazione.

Avviso

Le route relative non funzionano con le pagine definite in una classe sottoclassata Shell , che in genere è AppShell.xaml. Al contrario, solo le pagine registrate con il metodo Routing.RegisterRoute possono essere inserite nello stack di navigazione usando route relative. Per altre informazioni, vedere Registrare le route della pagina dei dettagli.

Navigazione contestuale

Le route relative abilitano la navigazione contestuale. Si consideri ad esempio la gerarchia di route seguente:

monkeys
  details
bears
  details

Quando viene visualizzata la pagina registrata per la monkeys route, passando alla details route verrà visualizzata la pagina registrata per la monkeys/details route. Analogamente, quando viene visualizzata la pagina registrata per la bears route, passando alla details route verrà visualizzata la pagina registrata per la bears/details route. Per informazioni su come registrare le route in questo esempio, vedere Registrare le route delle pagine.

Spostamento indietro

È possibile eseguire lo spostamento indietro specificando ".." come argomento per il GoToAsync metodo :

await Shell.Current.GoToAsync("..");

La navigazione all'indietro con ".." può anche essere combinata con un itinerario:

await Shell.Current.GoToAsync("../route");

In questo esempio viene eseguita la navigazione all'indietro e quindi si passa alla route specificata.

Importante

Navigare all'indietro e verso una route specificata è possibile solo se la navigazione all'indietro ti posiziona nel punto corrente della gerarchia di route per accedere alla route specificata.

Analogamente, è possibile spostarsi più volte all'indietro e quindi passare a una route specificata:

await Shell.Current.GoToAsync("../../route");

In questo esempio la navigazione all'indietro viene eseguita due volte e quindi si passa alla route specificata.

Inoltre, i dati possono essere passati tramite proprietà di query durante la navigazione all'indietro.

await Shell.Current.GoToAsync($"..?parameterToPassBack={parameterValueToPassBack}");

In questo esempio viene eseguita la navigazione all'indietro e il valore del parametro di query viene passato al parametro di query nella pagina precedente.

Note

I parametri di query possono essere aggiunti a qualsiasi richiesta di navigazione all'indietro.

Per maggiori informazioni sul passaggio dei dati durante la navigazione, vedere Passare i dati.

Route non valide

I formati di route seguenti non sono validi:

Formato Explanation
// pagina o ///page Le route globali attualmente non possono essere l'unica pagina nello stack di navigazione. Di conseguenza, il routing assoluto alle route globali non è supportato.

L'uso di questi formati di route comporta la visualizzazione di un'eccezione Exception .

Avviso

Se si tenta di passare a una route inesistente, viene generata un'eccezione ArgumentException .

Navigazione di debug

Alcune classi shell sono decorate con , DebuggerDisplayAttributeche specifica la modalità di visualizzazione di una classe o di un campo dal debugger. Ciò consente di eseguire il debug delle richieste di spostamento visualizzando i dati correlati alla richiesta di navigazione. Ad esempio, lo screenshot seguente mostra le CurrentItem proprietà e CurrentState dell'oggetto Shell.Current :

Screenshot del debugger.

In questo esempio, la CurrentItem proprietà di tipo FlyoutItem, visualizza il titolo e la route dell'oggetto FlyoutItem . Analogamente, la CurrentState proprietà di tipo ShellNavigationState, visualizza l'URI della route visualizzata all'interno dell'app Shell.

La classe Tab definisce una proprietà Stack di tipo IReadOnlyList<Page>, che rappresenta lo stack di navigazione corrente all'interno di Tab. La classe fornisce anche i seguenti metodi di navigazione sovrascrivibili:

Note

Tab.Stack è una raccolta di sola lettura. Non è possibile aggiungere, rimuovere o riordinare le pagine modificandola direttamente. Tutte le modifiche di spostamento devono passare attraverso GoToAsync. Per reimpostare lo stack di navigazione, usare una route assoluta , ad esempio //route. Per tornare indietro, usare la .. sintassi .

  • GetNavigationStack, restituisce IReadOnlyList<Page>, lo stack di navigazione corrente.
  • OnInsertPageBefore, viene chiamato quando INavigation.InsertPageBefore viene chiamato .
  • OnPopAsync, restituisce Task<Page>e viene chiamato quando INavigation.PopAsync viene chiamato .
  • OnPopToRootAsync, restituisce Taske viene chiamato quando INavigation.OnPopToRootAsync viene chiamato .
  • OnPushAsync, restituisce Taske viene chiamato quando INavigation.PushAsync viene chiamato .
  • OnRemovePage, viene chiamato quando INavigation.RemovePage viene chiamato .

Nell'esempio seguente viene illustrato come eseguire l'override del OnRemovePage metodo :

public class MyTab : Tab
{
    protected override void OnRemovePage(Page page)
    {
        base.OnRemovePage(page);

        // Custom logic
    }
}

In questo esempio, gli oggetti MyTab devono essere utilizzati nella gerarchia visiva della Shell anziché gli oggetti Tab.

La Shell classe definisce l'evento Navigating, che viene generato quando la navigazione sta per essere eseguita, sia a causa di una navigazione programmatica che dell'interazione dell'utente. L'oggetto ShellNavigatingEventArgs che accompagna l'evento Navigating fornisce le proprietà seguenti:

Proprietà Tipo Descrizione
Current ShellNavigationState URI della pagina corrente.
Source ShellNavigationSource Tipo di navigazione che si è verificato.
Target ShellNavigationState URI che rappresenta la destinazione della navigazione.
CanCancel bool Valore che indica se è possibile annullare la navigazione.
Cancelled bool Valore che indica se la navigazione è stata annullata.

Inoltre, la ShellNavigatingEventArgs classe fornisce un Cancel metodo che può essere usato per annullare la navigazione e un GetDeferral metodo che restituisce un ShellNavigatingDeferral token che può essere usato per completare la navigazione. Per altre informazioni sul rinvio dello spostamento, vedere Rinvio della navigazione.

La Shell classe definisce anche l'evento Navigated , che viene generato al termine della navigazione. L'oggetto ShellNavigatedEventArgs che accompagna l'evento Navigated fornisce le proprietà seguenti:

Proprietà Tipo Descrizione
Current ShellNavigationState URI della pagina corrente.
Previous ShellNavigationState URI della pagina precedente.
Source ShellNavigationSource Tipo di navigazione che si è verificato.

Importante

Il metodo OnNavigating viene chiamato quando l'evento Navigating viene generato. Analogamente, il metodo OnNavigated viene chiamato all'attivazione dell'evento Navigated. Entrambi i metodi possono essere sottoposti a override nella Shell sottoclasse per intercettare le richieste di navigazione.

Le classi ShellNavigatedEventArgs e ShellNavigatingEventArgs hanno entrambe proprietà Source, di tipo ShellNavigationSource. Questa enumerazione fornisce i valori seguenti:

  • Unknown
  • Push
  • Pop
  • PopToRoot
  • Insert
  • Remove
  • ShellItemChanged
  • ShellSectionChanged
  • ShellContentChanged

Pertanto, la navigazione può essere intercettata tramite un OnNavigating override, e azioni possono essere eseguite in base alla fonte di navigazione. Ad esempio, il codice seguente mostra come annullare lo spostamento indietro se i dati nella pagina non sono salvati:

protected override void OnNavigating(ShellNavigatingEventArgs args)
{
    base.OnNavigating(args);

    // Cancel any back navigation.
    if (args.Source == ShellNavigationSource.Pop)
    {
        args.Cancel();
    }
}

La navigazione della shell può essere intercettata e completata o annullata in base alla scelta dell'utente. A tale scopo, è possibile eseguire l'override del OnNavigating metodo nella Shell sottoclasse e chiamando il GetDeferral metodo sull'oggetto ShellNavigatingEventArgs . Questo metodo restituisce un ShellNavigatingDeferral token con un Complete metodo, che può essere usato per completare la richiesta di navigazione:

public MyShell : Shell
{
    // ...
    protected override async void OnNavigating(ShellNavigatingEventArgs args)
    {
        base.OnNavigating(args);

        ShellNavigatingDeferral token = args.GetDeferral();

        var result = await DisplayActionSheet("Navigate?", "Cancel", "Yes", "No");
        if (result != "Yes")
        {
            args.Cancel();
        }
        token.Complete();
    }    
}
public MyShell : Shell
{
    // ...
    protected override async void OnNavigating(ShellNavigatingEventArgs args)
    {
        base.OnNavigating(args);

        ShellNavigatingDeferral token = args.GetDeferral();

        var result = await DisplayActionSheetAsync("Navigate?", "Cancel", "Yes", "No");
        if (result != "Yes")
        {
            args.Cancel();
        }
        token.Complete();
    }    
}

In questo esempio viene visualizzata una finestra delle azioni che invita l'utente a completare la richiesta di navigazione o annullarla. La navigazione viene annullata richiamando il Cancel metodo sull'oggetto ShellNavigatingEventArgs . La navigazione viene completata richiamando il metodo Complete sul token ShellNavigatingDeferral che è stato recuperato dal metodo GetDeferral sull'oggetto ShellNavigatingEventArgs.

Avviso

Il metodo GoToAsync genererà un'eccezione InvalidOperationException se un utente tenta di spostarsi mentre è presente un rinvio di navigazione in sospeso.

Trasmettere i dati

I dati primitivi possono essere passati come parametri di query basati su stringa durante la navigazione programmabile basata su URI. Questa operazione viene ottenuta aggiungendo ? dopo una route, seguita da un ID parametro di query, =e un valore:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
    await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}");
}

In questo esempio viene recuperato l'elefante attualmente selezionato in CollectionView, e si passa al percorso elephantdetails, passando elephantName come parametro di query.

I dati primitivi possono anche essere passati come parte dell'URI di navigazione stesso, registrando una route che dichiara parametri di percorso. Per altre informazioni, vedere Modelli di route.

Trasmettere dati di navigazione a uso multiplo basati su oggetti

È possibile passare più dati di navigazione basati su oggetti con un GoToAsync overload che specifica un IDictionary<string, object> argomento:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Animal animal = e.CurrentSelection.FirstOrDefault() as Animal;
    var navigationParameter = new Dictionary<string, object>
    {
        { "Bear", animal }
    };
    await Shell.Current.GoToAsync($"beardetails", navigationParameter);
}

In questo esempio viene recuperato l'orso attualmente selezionato in CollectionView, come Animal. L'oggetto Animal viene aggiunto a un Dictionary oggetto con la chiave Bear. Viene quindi eseguita la navigazione sulla beardetails route, con l'oggetto Dictionary passato come parametro di navigazione.

Tutti i dati passati come IDictionary<string, object> argomento vengono conservati in memoria per la durata della pagina e non vengono rilasciati finché la pagina non viene rimossa dallo stack di navigazione. Questo può essere problematico, come illustrato nello scenario seguente:

  1. Page1 passa a Page2 usando il GoToAsync metodo , passando un oggetto denominato MyData. Page2 riceve MyData quindi come parametro di query.
  2. Page2 naviga verso Page3 utilizzando il metodo GoToAsync, senza trasmettere alcun dato.
  3. Page3 si sposta all'indietro con il GoToAsync metodo . Page2 quindi riceve MyData nuovamente come parametro di query.

Anche se questo è auspicabile in molti scenari, se non lo è, si dovrebbe cancellare l'argomento IDictionary<string, object> mediante il metodo Clear dopo che è stato ricevuto per la prima volta da una pagina.

Trasmettere i dati di navigazione basati su oggetti monouso

I dati di spostamento monouso basati su oggetti possono essere passati con un GoToAsync overload che specifica un ShellNavigationQueryParameters argomento. Un ShellNavigationQueryParameters oggetto è destinato a dati di navigazione ad uso singolo che vengono cancellati dopo che la navigazione è avvenuta. L'esempio seguente illustra la navigazione durante il passaggio di dati a uso singolo.

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Animal animal = e.CurrentSelection.FirstOrDefault() as Animal;
    var navigationParameter = new ShellNavigationQueryParameters
    {
        { "Bear", animal }
    };
    await Shell.Current.GoToAsync($"beardetails", navigationParameter);
}

In questo esempio viene recuperato l'orso attualmente selezionato in CollectionView, come oggetto Animal aggiunto all'oggetto ShellNavigationQueryParameters . Viene quindi eseguita la navigazione sulla beardetails route, con l'oggetto ShellNavigationQueryParameters passato come parametro di navigazione. Dopo che la navigazione è stata eseguita, i dati nell'oggetto ShellNavigationQueryParameters sono stati cancellati.

Ricevere i dati di navigazione

Esistono due approcci per ricevere i dati di navigazione:

  1. La classe che rappresenta la pagina navigata a, o la classe della pagina, può BindingContext essere decorata con un QueryPropertyAttribute per ogni parametro di query. Per altre informazioni, vedere Elaborare i dati di navigazione usando gli attributi delle proprietà di query.
  2. La classe che rappresenta la pagina a cui si naviga, o la classe per la pagina, può implementare BindingContext l'interfaccia IQueryAttributable. Per altre informazioni, vedere Elaborare i dati di navigazione usando un singolo metodo.

Elaborare i dati di navigazione usando gli attributi delle proprietà di query

I dati di navigazione possono essere ricevuti decorando la classe ricevente con un QueryPropertyAttribute per ciascun parametro di query basato su stringa, parametro di navigazione basato su oggetti o ShellNavigationQueryParameters oggetto:

[QueryProperty(nameof(Bear), "Bear")]
public partial class BearDetailPage : ContentPage
{
    Animal bear;
    public Animal Bear
    {
        get => bear;
        set
        {
            bear = value;
            OnPropertyChanged();
        }
    }

    public BearDetailPage()
    {
        InitializeComponent();
        BindingContext = this;
    }
}

In questo esempio il primo argomento per l'oggetto QueryPropertyAttribute specifica il nome della proprietà che riceverà i dati, con il secondo argomento che specifica l'ID parametro. Pertanto, QueryPropertyAttribute nell'esempio precedente viene specificato che la Bear proprietà riceverà i dati passati nel Bear parametro di navigazione nella chiamata al GoToAsync metodo.

Importante

I valori dei parametri di query basati su stringhe ricevuti tramite QueryPropertyAttribute vengono decodificati automaticamente tramite URL.

Avviso

La ricezione dei dati di navigazione tramite QueryPropertyAttribute non è sicura per il trimming e non deve essere usata con trimming completo o NativeAOT. È invece necessario implementare l'interfaccia IQueryAttributable sui tipi che devono accettare parametri di query. Per altre informazioni, vedere Elaborare i dati di spostamento usando un singolo metodo, Ottimizzare un'app .NET MAUI e Distribuzione nativa di AOT.

Elaborare i dati di navigazione usando un singolo metodo

I dati di navigazione possono essere ricevuti implementando l'interfaccia IQueryAttributable nella classe ricevente. L'interfaccia IQueryAttributable specifica che la classe di implementazione deve implementare il ApplyQueryAttributes metodo . Questo metodo ha un query argomento di tipo IDictionary<string, object>, che contiene tutti i dati passati durante la navigazione. Ogni chiave nel dizionario è un ID parametro di query, con il relativo valore corrispondente all'oggetto che rappresenta i dati. Il vantaggio di usare questo approccio è che i dati di navigazione possono essere elaborati usando un unico metodo, che può essere utile quando si dispone di più elementi di dati di navigazione che richiedono l'elaborazione nel suo complesso.

L'esempio seguente mostra una classe del modello di visualizzazione che implementa l'interfaccia IQueryAttributable :

public class MonkeyDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Monkey { get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        Monkey = query["Monkey"] as Animal;
        OnPropertyChanged("Monkey");
    }
    ...
}

In questo esempio, il ApplyQueryAttributes metodo recupera l'oggetto che corrisponde alla Monkey chiave nel query dizionario, che è stato passato come argomento alla chiamata al GoToAsync metodo.

Importante

I valori dei parametri di query basati su stringhe ricevuti tramite l'interfaccia non vengono decodificati automaticamente nell'URL IQueryAttributable .

Passare ed elaborare più elementi di dati

È possibile passare più parametri di query basati su stringhe collegandoli con &. Ad esempio, il codice seguente passa due elementi di dati:

async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
    string elephantLocation = (e.CurrentSelection.FirstOrDefault() as Animal).Location;
    await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}&location={elephantLocation}");
}

Questo esempio di codice recupera l'elefante attualmente selezionato in CollectionView, e naviga verso la route elephantdetails, passando elephantName e elephantLocation come parametri di query.

Per ricevere più elementi di dati, la classe che rappresenta la pagina a cui si viene reindirizzati oppure la classe della pagina stessa può essere decorata con un BindingContext per ogni parametro di query basato su stringa:

[QueryProperty(nameof(Name), "name")]
[QueryProperty(nameof(Location), "location")]
public partial class ElephantDetailPage : ContentPage
{
    public string Name
    {
        set
        {
            // Custom logic
        }
    }

    public string Location
    {
        set
        {
            // Custom logic
        }
    }
    ...    
}

In questo esempio, la classe è decorata con un QueryPropertyAttribute per ogni parametro di query. Il primo QueryPropertyAttribute specifica che la Name proprietà riceverà i dati passati nel name parametro di query, mentre il secondo QueryPropertyAttribute specifica che la Location proprietà riceverà i dati passati nel location parametro di query. In entrambi i casi, i valori dei parametri di query vengono specificati nell'URI nella chiamata al GoToAsync metodo.

Avviso

La ricezione dei dati di navigazione tramite QueryPropertyAttribute non è sicura per il trimming e non deve essere usata con trimming completo o NativeAOT. È invece necessario implementare l'interfaccia IQueryAttributable sui tipi che devono accettare parametri di query. Per altre informazioni, vedere Ridurre un'app .NET MAUI e Distribuzione AOT nativa.

In alternativa, i dati di navigazione possono essere elaborati da un metodo unico implementando l'interfaccia IQueryAttributable nella classe che rappresenta la pagina a cui si passa o la classe per la BindingContext.

public class ElephantDetailViewModel : IQueryAttributable, INotifyPropertyChanged
{
    public Animal Elephant { get; private set; }

    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        string name = HttpUtility.UrlDecode(query["name"].ToString());
        string location = HttpUtility.UrlDecode(query["location"].ToString());
        ...        
    }
    ...
}

In questo esempio, il metodo ApplyQueryAttributes recupera il valore dei parametri di query name e location dall'URI nella chiamata al metodo GoToAsync.

Note

È possibile passare contemporaneamente parametri di query basati su stringhe e parametri di navigazione basati su oggetti durante l'esecuzione della navigazione basata su route.

Comportamento del pulsante Indietro

È possibile ridefinire l'aspetto e il comportamento del pulsante Indietro impostando la BackButtonBehavior proprietà associata su un BackButtonBehavior oggetto . La classe BackButtonBehavior definisce le proprietà seguenti:

  • Command, di tipo ICommand, che viene eseguito quando viene premuto il pulsante indietro.
  • CommandParameter, di tipo object, che è il parametro passato a Command.
  • IconOverride, di tipo ImageSource, l'icona usata per il pulsante Indietro.
  • IsEnabled, di tipo boolean, indica se il pulsante Indietro è abilitato. Il valore predefinito è true.
  • IsVisible, di tipo boolean, indica se il pulsante Indietro è visibile. Il valore predefinito è true.
  • TextOverride, di tipo string, il testo usato per il pulsante Indietro.

A partire da .NET MAUI 11, BackButtonBehavior definisce anche una proprietà AccessibilityLabel di tipo string, che imposta l'etichetta di accessibilità che gli screen reader leggono per il pulsante Indietro. Questo è indipendente da TextOverride, quindi l'etichetta visibile può rimanere breve mentre quella letta ad alta voce resta descrittiva. L'esempio seguente imposta un'icona personalizzata e un'etichetta di accessibilità insieme:

<ContentPage ...>
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding BackCommand}"
                            IconOverride="back.png"
                            AccessibilityLabel="Back to order list" />
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

Si consiglia di impostare un AccessibilityLabel esplicito ogni volta che IconOverride è impostato o TextOverride è nascosto, perché altrimenti gli screen reader annuncerebbero un valore generico o nessun valore. Per altre indicazioni sull'accessibilità, vedere Accessibilità.

Tutte queste proprietà sono supportate da BindableProperty oggetti , il che significa che le proprietà possono essere destinazioni di data binding. Ogni BindableProperty oggetto ha una OneTime modalità di binding, ciò significa che i dati passano dall'origine alla destinazione, ma solo quando cambia BindingContext .

Tutte queste proprietà sono supportate da BindableProperty oggetti , il che significa che le proprietà possono essere destinazioni di data binding. Gli Command, CommandParameter, IconOveride e TextOverideBindableProperty hanno OneTime modalità di binding, il che significa che i dati vengono trasferiti dall'origine alla destinazione solo quando BindingContext viene modificato. Gli IsEnabled oggetti e IsVisibleBindableProperty hanno OneWay modalità di associazione, il che significa che i dati passano dall'origine alla destinazione.

Il codice seguente mostra un esempio di ridefinizione dell'aspetto e del comportamento del pulsante Indietro:

<ContentPage ...>    
    <Shell.BackButtonBehavior>
        <BackButtonBehavior Command="{Binding BackCommand}"
                            IconOverride="back.png" />   
    </Shell.BackButtonBehavior>
    ...
</ContentPage>

La Command proprietà è impostata su un ICommand oggetto da eseguire quando viene premuto il pulsante Indietro e la IconOverride proprietà è impostata sull'icona usata per il pulsante Indietro:

Screenshot di sostituzione dell'icona del pulsante Indietro della Shell.