Navigation
Tipp
Diese Inhalte sind ein Auszug aus dem eBook „Enterprise Application Patterns Using .NET MAUI“, verfügbar unter .NET Docs oder als kostenlos herunterladbare PDF-Datei, die offline gelesen werden kann.
.NET MAUI umfasst Unterstützung für Seitennavigation, die sich in der Regel aus der Interaktion des Benutzers mit der Benutzeroberfläche oder der App selbst als Ergebnis interner logikgesteuerter Zustandsänderungen ergibt. Die Implementierung der Navigation in Apps, die das Model-View-ViewModel-Muster (MVVM) verwenden, kann jedoch komplex sein, da die folgenden Herausforderungen zu bewältigen sind:
- Identifizieren der Ansicht, zu der navigiert werden soll, unter Verwendung eines Ansatzes, der keine enge Kopplung und Abhängigkeiten zwischen den Ansichten einführt.
- Koordinieren des Prozesses, durch den die Ansicht, zu der navigiert werden soll, instanziiert und initialisiert wird. Bei Verwendung von MVVM müssen die Ansicht und das Ansichtsmodell instanziiert und über den Bindungskontext der Ansicht miteinander verknüpft werden. Wenn eine App einen Dependency Injection-Container verwendet, erfordert die Instanziierung von Ansichten und Ansichtsmodellen möglicherweise einen bestimmten Konstruktionsmechanismus.
- Entscheiden, ob die Ansichtsnavigation zuerst oder die Ansichtmodellnavigation zuerst ausgeführt werden soll. Wenn Ansichtsnavigation zuerst verwendet wird, verweist die Seite, zu der navigiert werden soll, auf den Namen des Ansichtstyps. Während der Navigation wird die angegebene Ansicht zusammen mit dem entsprechenden Ansichtsmodell und anderen abhängigen Diensten instanziiert. Ein alternativer Ansatz besteht darin, Ansichtsmodellnavigation zuerst zu verwenden, wobei die Seite, zu der navigiert werden soll, auf den Namen des Ansichtsmodelltyps verweist.
- Bestimmen, wie das Navigationsverhalten der App sauber zwischen den Ansichten und Ansichtsmodellen getrennt wird. Das MVVM-Muster trennt die Benutzeroberfläche der App von der Präsentations- und Geschäftslogik, bietet aber keinen direkten Mechanismus, um sie miteinander zu verknüpfen. Das Navigationsverhalten einer App erstreckt sich jedoch häufig über die Benutzeroberfläche und Präsentationskomponenten der App. Der Benutzer initiiert die Navigation häufig über eine Ansicht, und die Ansicht wird als Ergebnis der Navigation ersetzt. Die Navigation muss jedoch häufig auch innerhalb des Ansichtsmodells initiiert oder koordiniert werden.
- Bestimmen, wie Parameter während der Navigation zu Initialisierungszwecken übergeben werden. Wenn der Benutzer beispielsweise zu einer Ansicht navigiert, um Bestelldetails zu aktualisieren, müssen die Bestelldaten an die Ansicht übergeben werden, damit sie die richtigen Daten anzeigen kann.
- Koordinieren der Navigation, um sicherzustellen, dass bestimmte Geschäftsregeln eingehalten werden. So können Benutzer beispielsweise aufgefordert werden, ungültige Daten zu korrigieren, bevor sie eine Ansicht verlassen, oder sie werden aufgefordert, Datenänderungen, die in der Ansicht vorgenommen wurden, zu übermitteln oder zu verwerfen.
In diesem Kapitel werden diese Herausforderungen behandelt, indem eine Navigationsdienstklasse mit dem Namen MauiNavigationService
vorgestellt wird, die zum Ausführen von Ansichtsmodell-Seitennavigation zuerst verwendet wird.
Hinweis
Der von der App verwendete MauiNavigationService
ist einfach und deckt nicht alle möglichen Navigationstypen ab. Die von Ihrer Anwendung benötigten Navigationstypen erfordern möglicherweise zusätzliche Funktionen.
Navigieren zwischen Seiten
Die Navigationslogik kann sich im CodeBehind einer Ansicht oder in einem datengebundenen Ansichtsmodell befinden. Das Platzieren der Navigationslogik in einer Ansicht ist zwar der einfachste Ansatz, lässt sich aber nicht ohne weiteres durch Komponententests testen. Wenn Sie Navigationslogik in Ansichtsmodellklassen platzieren, kann die Logik durch Komponententests überprüft werden. Darüber hinaus kann das Ansichtsmodell dann Logik implementieren, um die Navigation zu steuern, um sicherzustellen, dass bestimmte Geschäftsregeln erzwungen werden. Beispielsweise erlaubt eine App dem Benutzer möglicherweise nicht, von einer Seite zu navigieren, ohne vorher sicherzustellen, dass die eingegebenen Daten gültig sind.
Ein Navigationsdienst wird in der Regel über Ansichtsmodelle aufgerufen, um die Testbarkeit zu fördern. Wenn Sie jedoch von Ansichtsmodellen zu Ansichten navigieren, müssen die Ansichtsmodelle auf Ansichten verweisen, insbesondere auf Ansichten, denen das aktive Ansichtsmodell nicht zugeordnet ist, was nicht empfohlen wird. Daher gibt der hier dargestellte MauiNavigationService
den Ansichtsmodelltyp als Ziel an, zu dem navigiert werden soll.
Die multiplattformfähige eShop-App verwendet die MauiNavigationService
-Klasse, um Ansichtsmodellnavigation zuerst bereitzustellen. Diese Klasse implementiert die INavigationService
-Schnittstelle, die im folgenden Codebeispiel gezeigt wird:
public interface INavigationService
{
Task InitializeAsync();
Task NavigateToAsync(string route, IDictionary<string, object> routeParameters = null);
Task PopAsync();
}
Diese Schnittstelle gibt an, dass eine implementierende Klasse die folgenden Methoden bereitstellen muss:
Methode | Zweck |
---|---|
InitializeAsync |
Führt beim Starten der App die Navigation zu einer von zwei Seiten aus. |
NavigateToAsync(string route, IDictionary<string, object> routeParameters = null) |
Führt hierarchische Navigation zu einer angegebenen Seite mithilfe einer registrierten Navigationsroute aus. Kann optional benannte Routenparameter übergeben, die für die Verarbeitung auf der Zielseite verwendet werden. |
PopAsync |
Entfernt die aktuelle Seite aus dem Navigationsstapel. |
Hinweis
Eine INavigationService
-Schnittstelle gibt normalerweise auch eine GoBackAsync
-Methode an, die verwendet wird, um programmgesteuert zur vorherigen Seite im Navigationsstapel zurückzukehren. Diese Methode fehlt jedoch in der multiplattformfähigen eShop-App, da sie nicht erforderlich ist.
Erstellen der MauiNavigationService-Instanz
Die MauiNavigationService
-Klasse, die die INavigationService
-Schnittstelle implementiert, wird als Singleton mit dem Dependency Injection-Container in der MauiProgram.CreateMauiApp()
-Methode registriert, wie im folgenden Codebeispiel gezeigt:
mauiAppBuilder.Services.AddSingleton<INavigationService, MauiNavigationService>();;
Die INavigationService
-Schnittstelle kann dann aufgelöst werden, indem sie dem Konstruktor unserer Ansichten und Ansichtsmodelle hinzugefügt wird, wie im folgenden Codebeispiel gezeigt:
public AppShell(INavigationService navigationService)
Dadurch wird ein Verweis auf das MauiNavigationService
-Objekt zurückgegeben, das im Dependency Injection-Container gespeichert ist.
Die ViewModelBase
-Klasse speichert die MauiNavigationService
-Instanz in einer NavigationService
-Eigenschaft vom Typ INavigationService
. Daher können alle Ansichtsmodellklassen, die von der ViewModelBase
-Klasse abgeleitet werden, die NavigationService
-Eigenschaft verwenden, um auf die von der INavigationService
-Schnittstelle angegebenen Methoden zuzugreifen.
Verarbeiten von Navigationsanforderungen
.NET MAUI bietet mehrere Möglichkeiten, innerhalb einer Anwendung zu navigieren. Die herkömmliche Navigation erfolgt mit der NavigationPage
-Klasse, die eine hierarchische Navigationsumgebung implementiert, in der der Benutzer je nach Wunsch vorwärts und rückwärts durch Seiten navigieren kann. Die eShop-App verwendet die Shell
-Komponente als Stammcontainer für die Anwendung und als Navigationshost. Weitere Informationen zur Shellnavigation finden Sie unter Shellnavigation in Microsoft Developer Center.
Die Navigation erfolgt in Ansichtsmodellklassen, indem eine der NavigateToAsync
-Methoden aufgerufen wird, wobei der Routenpfad für die Seite angegeben wird, zu der navigiert wird, wie im folgenden Codebeispiel gezeigt:
await NavigationService.NavigateToAsync("//Main");
Das folgende Codebeispiel zeigt die NavigateToAsync
-Methode, die von der MauiNavigationService
-Klasse bereitgestellt wird:
public Task NavigateToAsync(string route, IDictionary<string, object> routeParameters = null)
{
return
routeParameters != null
? Shell.Current.GoToAsync(route, routeParameters)
: Shell.Current.GoToAsync(route);
}
Das .NET MAUI-Shell
-Steuerelement ist bereits mit routenbasierter Navigation vertraut, sodass die NavigateToAsync
-Methode diese Funktionalität maskiert. Die NavigateToAsync
-Methode ermöglicht es, Navigationsdaten als Argument anzugeben, das an das Ansichtsmodell übergeben wird, zu dem navigiert wird, wo es normalerweise zum Ausführen der Initialisierung verwendet wird. Weitere Informationen finden Sie unter Übergeben von Parametern während der Navigation.
Wichtig
Es gibt mehrere Möglichkeiten, die Navigation in .NET MAUI auszuführen. Der MauiNavigationService
ist speziell für das Arbeiten mit der Shell
konzipiert. Wenn Sie ein NavigationPage
- oder TabbedPage
-Objekt oder einen anderen Navigationsmechanismus verwenden, muss dieser Routingdienst aktualisiert werden, um diese Komponenten zu verwenden.
Um Routen für den MauiNavigationService
zu registrieren, müssen wir Routeninformationen aus XAML oder im CodeBehind bereitstellen. Das folgende Beispiel zeigt die Registrierung von Routen über XAML.
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:eShop.Views"
x:Class="eShop.AppShell">
<!-- Omitted for brevity -->
<FlyoutItem >
<ShellContent x:Name="login" ContentTemplate="{DataTemplate views:LoginView}" Route="Login" />
</FlyoutItem>
<TabBar x:Name="main" Route="Main">
<ShellContent Title="CATALOG" Route="Catalog" Icon="{StaticResource CatalogIconImageSource}" ContentTemplate="{DataTemplate views:CatalogView}" />
<ShellContent Title="PROFILE" Route="Profile" Icon="{StaticResource ProfileIconImageSource}" ContentTemplate="{DataTemplate views:ProfileView}" />
</TabBar>
</Shell>
In diesem Beispiel legen die ShellContent
- undTabBar
-Benutzeroberflächenobjekte ihre Route
-Eigenschaft fest. Dies ist die bevorzugte Methode zum Registrieren von Routen für Benutzeroberflächenobjekte, die von einer Shell
gesteuert werden.
Wenn Objekte vorhanden sind, die dem Navigationsstapel zu einem späteren Zeitpunkt hinzugefügt werden sollen, müssen diese über CodeBehind hinzugefügt werden. Das folgende Beispiel zeigt die Registrierung von Routen in CodeBehind.
Routing.RegisterRoute("Filter", typeof(FiltersView));
Routing.RegisterRoute("Basket", typeof(BasketView));
In CodeBehind rufen wir die Routing.RegisterRoute
-Methode auf, die einen Routennamen als ersten Parameter und einen Ansichtstyp als zweiten Parameter annimmt. Wenn ein Ansichtsmodell die NavigationService
-Eigenschaft zum Navigieren verwendet, sucht das Shell
-Objekt der Anwendung nach registrierten Routen und pusht sie auf den Navigationsstapel.
Nachdem die Ansicht erstellt und zu ihr navigiert wurde, werden die ApplyQueryAttributes
- und InitializeAsync
-Methoden des zugeordneten Ansichtsmodells der Ansicht ausgeführt. Weitere Informationen finden Sie unter Übergeben von Parametern während der Navigation.
Navigieren beim Starten der App
Wenn die App gestartet wird, wird ein Shell
-Objekt als Stammansicht der Anwendung festgelegt. Nach dem Festlegen wird die Shell
zum Steuern der Routenregistrierung verwendet und ist anschließend im Stamm der Anwendung vorhanden. Nachdem die Shell
erstellt wurde, können wir warten, bis sie mit der OnParentSet
-Methode zur Initialisierung unserer Navigationsroute an die Anwendung angefügt wird. Im folgenden Codebeispiel wird diese Methode veranschaulicht:
protected override async void OnParentSet()
{
base.OnParentSet();
if (Parent is not null)
{
await _navigationService.InitializeAsync();
}
}
Die Methode verwendet eine Instanz von INavigationService
, der der Konstruktor aus der Dependency Injection zur Verfügung gestellt wird, und ruft die InitializeAsync
-Methode auf.
Im folgenden Codebeispiel wird eine Implementierung der MauiNavigationService.InitializeAsync
-Methode gezeigt:
public Task InitializeAsync()
{
return NavigateToAsync(string.IsNullOrEmpty(_settingsService.AuthAccessToken)
? "//Login"
: "//Main/Catalog");
}
Zur //Main/Catalog
-Route wird navigiert, wenn die App über ein zwischengespeichertes Zugriffstoken verfügt, das für die Authentifizierung verwendet wird. Andernfalls wird zur //Login
-Route navigiert.
Übergeben von Parametern während der Navigation
Die NavigateToAsync
-Methode, die von derINavigationService
-Schnittstelle angegeben wird, ermöglicht es, Navigationsdaten als ein IDictionary<string, object>
von Daten anzugeben, das an das Ansichtsmodell übergeben wird, zu dem navigiert wird, wo es normalerweise zum Ausführen der Initialisierung verwendet wird.
Beispielsweise enthält die ProfileViewModel
-Klasse einen OrderDetailCommand
, der ausgeführt wird, wenn der Benutzer eine Bestellung auf der ProfileView
-Seite auswählt. Dadurch wird wiederum die OrderDetailAsync
-Methode ausgeführt, wie im folgenden Codebeispiel gezeigt:
private async Task OrderDetailAsync(Order order)
{
if (order is null)
{
return;
}
await NavigationService.NavigateToAsync(
"OrderDetail",
new Dictionary<string, object>{ { "OrderNumber", order.OrderNumber } });
}
Diese Methode ruft die Navigation zur OrderDetail
-Route auf und übergibt die Bestellnummerinformationen der vom Benutzer ausgewählten Bestellung. Wenn das Framework für Dependency Injection die OrderDetailView
für die OrderDetail
-Route zusammen mit der OrderDetailViewModel
-Klasse erstellt, die dem BindingContext
der Ansicht zugewiesen ist. Dem OrderDetailViewModel
wurde ein Attribut hinzugefügt, das es ermöglicht, Daten vom Navigationsdienst zu empfangen, wie im folgenden Codebeispiel gezeigt.
[QueryProperty(nameof(OrderNumber), "OrderNumber")]
public class OrderDetailViewModel : ViewModelBase
{
public int OrderNumber { get; set; }
}
Mit dem Attribut QueryProperty
können wir einen Parameter für eine Eigenschaft angeben, der Werte zugeordnet werden sollen, und einen Schlüssel, um Werte aus dem Wörterbuch der Abfrageparameter zu ermitteln. In diesem Beispiel wurden während des Aufrufs von NavigateToAsync
der Schlüssel OrderNumber und der Wert der Bestellnummer bereitgestellt. Das Ansichtsmodell hat den Schlüssel OrderNumber gefunden und den Wert der OrderNumber
-Eigenschaft zugeordnet. Die OrderNumber
-Eigenschaft kann dann zu einem späteren Zeitpunkt verwendet werden, um die vollständigen Bestelldetails aus der OrderService
-Instanz abzurufen.
Aufrufen der Navigation mithilfe von Verhalten
Die Navigation wird normalerweise von einer Ansicht aus durch eine Benutzerinteraktion ausgelöst. Beispielsweise führt die LoginView
Navigation nach erfolgreicher Authentifizierung aus. Das folgende Codebeispiel zeigt, wie die Navigation durch Verhalten aufgerufen wird:
<WebView>
<WebView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="Navigating"
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</WebView.Behaviors>
</WebView>
Zur Laufzeit reagiert das EventToCommandBehavior
auf die Interaktion mit der WebView
. Wenn die WebView
zu einer Webseite navigiert, wird das Navigating
-Ereignis ausgelöst, das den NavigateCommand
im LoginViewMode
ausführt. Die Ereignisargumente für das Ereignis werden standardmäßig an den Befehl übergeben. Diese Daten werden konvertiert, während sie von dem in der EventArgsConverter
-Eigenschaft angegebenen Konverter zwischen Quelle und Ziel übergeben werden, der die Url
aus den WebNavigatingEventArgs
zurückgibt. Daher wird beim Ausführen von NavigationCommand
die Url
der Webseite als Parameter an die registrierte Aktion übergeben.
Dadurch führt NavigationCommand
wiederum die NavigateAsync
-Methode aus, wie im folgenden Codebeispiel gezeigt:
private async Task NavigateAsync(string url)
{
// Omitted for brevity.
if (!string.IsNullOrWhiteSpace(accessToken))
{
_settingsService.AuthAccessToken = accessToken;
_settingsService.AuthIdToken = authResponse.IdentityToken;
await NavigationService.NavigateToAsync("//Main/Catalog");
}
}
Mit dieser Methode wird NavigationService
aufgerufen und die Anwendung an die //Main/Catalog
-Route weitergeleitet.
Bestätigen oder Abbrechen der Navigation
Eine App muss möglicherweise während eines Navigationsvorgangs mit dem Benutzer interagieren, damit der Benutzer die Navigation bestätigen oder abbrechen kann. Dies kann beispielsweise erforderlich dann sein, wenn der Benutzer versucht, zu navigieren, bevor er eine Dateneingabeseite vollständig abgeschlossen hat. Unter diesen Umständen sollte eine App eine Benachrichtigung bereitstellen, die dem Benutzer erlaubt, von der Seite zu navigieren oder den Navigationsvorgang abzubrechen, bevor er auftritt. Dies kann in einer Ansichtsmodellklasse erreicht werden, indem die Antwort von einer Benachrichtigung verwendet wird, um zu steuern, ob die Navigation aufgerufen wird.
Zusammenfassung
.NET MAUI bietet Unterstützung für Seitennavigation, die sich in der Regel aus der Interaktion des Benutzers mit der Benutzeroberfläche oder der App selbst aufgrund interner logikgesteuerter Zustandsänderungen ergibt. Allerdings kann die Navigation in Anwendungen, die das MVVM-Muster verwenden, kompliziert zu implementieren sein.
In diesem Kapitel wurde eine NavigationService-Klasse vorgestellt, die verwendet wird, um die Ansichtsmodellnavigation zuerst aus Ansichtsmodellen auszuführen. Das Platzieren von Navigationslogik in Ansichtsmodellklassen bedeutet, dass die Logik durch automatisierte Tests ausgeführt werden kann. Darüber hinaus kann das Ansichtsmodell dann Logik implementieren, um die Navigation zu steuern, um sicherzustellen, dass bestimmte Geschäftsregeln erzwungen werden.