Navigation

Das .NET MAUI App Template nutzt unseren ShellNavigator. Dies ist eine Helper-Klasse, welche alle Funktionen rund um die Shell-Navigation in .NET MAUI vereint. Verfügbare Navigationsarten Parameterlose Navigation Grundsätzlich wird unterschieden, ob der Navigation keine, ein oder mehrere Parameter mit der Navigation mitgegeben werden. Eine parameterlose Navigation wechselt einfach von der aktuellen Seite zur der angegeben…

Image Description

The .NET MAUI App Template uses our ShellNavigator. This is a helper class that combines all functions related to shell navigation in .NET MAUI.

Available navigation types

Parameterless navigation

Basically, a distinction is made as to whether no, one or more parameters are given with the navigation. Parameterless navigation simply switches from the current page to the specified page.

Example of a parameterless navigation

_ = await ShellNavigator.Instance.GoToAsync(
    $"///{ShellNavigator.Instance.RootPage}",
    false,
    DeviceInfo.Platform == DevicePlatform.Android ? 150 : 50
    );

Here, the LoadingPageViewModel is forwarded to the defined start page. The name of the page to be navigated is specified, as well as whether the process is animated (here false) and whether a waiting time is necessary.

Navigation mit QueryParameters

Im Gegenzug zu .NET MAUI können in Xamarin.Forms keine komplexen Objekte (query parameters) mit übergeben werden. Es ist empfohlen, die Objekte in einen String zu serialisieren.

Beispiel einer Navigation mit Parametern

_ = await ShellNavigator.Instance.GoToAsync(ShellRoute.NewStockToDepotModalPage, new Dictionary<string, object> {
    { "depot", CurrentDepot },
});

In diesem Beispiel führen wir eine Navigation aus um eine neue Aktie zu einem Depot hinzuzufügen. Als Parameter wird hier immer ein Wörterbuch mit einem Schlüssel (Name des Parameters) und ein Objekt übergeben. In diesem Fall übergeben wir das Depot, in welches wir eine neue Aktie einfügen möchten. In der GoToAsync Methode wird der Parameter jedoch serialisiert.

 string parameterString = string.Empty;
 int i = 0;
 parameters?.ForEach((p) =>
 {
     if (p.Value is Guid guid)
         parameterString += $"{p.Key}={guid}";
     else if (p.Value is string plaintext)
         parameterString += $"{p.Key}={plaintext.Replace("\"", string.Empty)}";
     else if (p.Value is IList<Depot> items)
         // Use json convert, otherwise line breaks are inside the string.
         parameterString += $"{p.Key}={JsonConvert.SerializeObject(items.Select(d => d.Id))}";
     else
         parameterString += $"{p.Key}={JsonConvert.SerializeObject(p.Value)}";
     
     if (i < parameters.Count - 1)
         parameterString += "&";
     i++;
 });
 parameterString = parameterString.Trim();
 await Shell.Current.GoToAsync(state: $"{target}?{parameterString}", animate: animate);

Wird eine Liste von Objekten übergeben, wie hier im Beispiel die List<Depot>, dann wird die Liste auf die Guid reduziert und serialisiert.

However, the recipient ViewModel must be prepared for this parameter. This is done with the [QueryProperty…] attribute, as shown in the example below.

[QueryProperty(nameof(SelectedDepotJson), "depot")]
[QueryProperty(nameof(SelectedDepotsJson), "depots")]
public partial class NewStockToDepotModalPageViewModel : AppViewModel// BaseViewModel
{
    #region Parameters
    
    [ObservableProperty]
    string selectedDepotJson;
    partial void OnSelectedDepotJsonChanged(string value)
    {
        SelectedDepot = JsonConvertHelper.ToObject<SelectedDepot>(value);
    }
    
    [ObservableProperty]
    protected Depot selectedDepot;
    
    [ObservableProperty]
    string selectedDepotsJson;
    partial void OnSelectedDepotJsonChanged(string value)
    {
        if (!string.IsNullOrEmpty(value))
		{
    		List<Guid> depotIds = JsonConvertHelper.ToObject<List<Guid>>(value);
    		SelectedDepots = new(Depots.Where(c => depotIds.Contains(c.Id)));
		}
    }
    
    [ObservableProperty]
    protected List<Depot> selectedDepots;
    #endregion
}   

Erfolgt eine Navigation mit dem Parameter “depot”, so wird dieser beim Aufrufen an das ViewModel übergeben und gesetzt.

Navigate back

Das Ganze funktioniert auch Rückwärts, sprich wenn der Benutzer eine Seite verlässt und dazu den “Zurück”-Button nutzt. Auch hier kann ein Parameter rückwärts an den vorherigen Aufrufer übergeben werden. Dies ist vor allem dann sinnvoll, wenn in der aktuellen Ansicht zum Beispiel eine neue Aktie erstellt wurde und diese gleich in der vorherigen Ansicht verwendet werden soll.

_ = await ShellNavigator.Instance.GoToAsync($"..", new Dictionary<string, object>() { { "stockfound", SelectedItem } });

Im obigen Beispiel wird durch die zwei Punkte “..” eine Rückwärtsnavigation eingeleitet. Dabei wird zum Beispiel die gefundene Aktie an die Aufrufer-Seite zurückgegeben. Auch hier gilt, dass das ViewModel der Aufrufer-Seite auf den Empfang des Parameters “stockfound” vorbereitet sein muss.

await ShellNavigator.Instance.GoBackAsync(new Dictionary<string, object>()
{
    { "hcc", hex },
});

Ein anderes Beispiel ist die direkte “GoBackAsync” Funktion. Diese unterstützt ebenfalls die Übergabe von Parametern und erfüllt den gleichen Zweck wie das erste Beispiel mit den zwei Punkten.

Shell routes and registration of new routes

To keep navigation simple and clear, the app uses ShellRouten (enum).

public enum ShellRoute
{
    LoadingPage,

    // App
    SettingsPage,
    PrivacyPage,
    AboutPage,
    VersionDetailPage,
    ProjectOverviewPage,
    //...
}

Diese Spiegeln einfach den Namen der verfügbaren Seiten wieder. Im Quellcode kann dann einfach über den Zugriff auf den Enum die gewünschte Seite aufgerufen werden. Alternativ kann auch ein String übergeben werden, welcher über die Funktion “nameof(MyPage)” die gewünschte Zielseite definiert.

Register new route

Solltet Ihr eine neue Seite zu der App hinzufügen, dann muss diese im ShellNavigator unter der Methode “RegisterRoutes()” eingetragen werden, da der Navigator sonst die Route nicht identifizieren kann.

public void RegisterRoutes()
{
    Routing.RegisterRoute(nameof(AboutPage), typeof(AboutPage));
    Routing.RegisterRoute(nameof(SettingsPage), typeof(SettingsPage));
    Routing.RegisterRoute(nameof(PrivacyPage), typeof(PrivacyPage));
    Routing.RegisterRoute(nameof(VersionDetailPage), typeof(VersionDetailPage));
    Routing.RegisterRoute(nameof(ProjectOverviewPage), typeof(ProjectOverviewPage));
    Routing.RegisterRoute(nameof(LoadingPage), typeof(LoadingPage));
    //...
}

Was this article helpful to you?

Yes No

Related Articles