Freigeben über


Navigation für Unternehmens-Apps

Hinweis

Dieses eBook wurde im Frühjahr 2017 veröffentlicht und wurde seitdem nicht aktualisiert. Es gibt vieles im Buch, das wertvoll bleibt, aber ein Teil des Materials ist veraltet.

Xamarin.Forms umfasst unterstützung für die 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, mit einem Ansatz, der keine enge Kopplung und Abhängigkeiten zwischen Ansichten einführt.
  • Hier erfahren Sie, wie Sie den Prozess koordinieren, mit dem die zu navigierende Ansicht 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 Abhängigkeitsinjektionscontainer verwendet, erfordert die Instanziierung von Ansichten und Ansichtsmodellen möglicherweise einen bestimmten Konstruktionsmechanismus.
  • Gibt an, ob die Ansichtsnavigation oder die Modell-First-Navigation angezeigt 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 in der Ansichtsmodell-first-Navigation, bei der die zu navigierende Seite auf den Namen des Ansichtsmodelltyps verweist.
  • So trennen Sie das Navigationsverhalten der App sauber zwischen den Ansichten und Ansichtsmodellen. Das MVVM-Muster bietet eine Trennung zwischen der Benutzeroberfläche der App und ihrer Präsentations- und Geschäftslogik. Das Navigationsverhalten einer App erstreckt sich jedoch häufig über die Ui- und Präsentationsteile 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.
  • Übergeben von Parametern während der Navigation zu Initialisierungszwecken 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 NavigationService Klasse präsentiert wird, die verwendet wird, um die Navigation auf der ersten Seite anzuzeigen.

Hinweis

Die NavigationService von der App verwendete ist nur für die hierarchische Navigation zwischen ContentPage-Instanzen konzipiert. Die Verwendung des Diensts zum Navigieren zwischen anderen Seitentypen kann zu unerwartetem Verhalten führen.

Die Navigationslogik kann sich im CodeBehind einer Ansicht oder in einem datengebundenen Ansichtsmodell befinden. Das Platzieren von Navigationslogik in einer Ansicht ist zwar der einfachste Ansatz, aber es ist nicht einfach durch Komponententests zu testen. Das Platzieren von Navigationslogik in Ansichtsmodellklassen bedeutet, dass die Logik über Komponententests ausgeübt werden kann. Darüber hinaus kann das Ansichtsmodell 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.

Eine NavigationService Klasse wird in der Regel aus Ansichtsmodellen aufgerufen, um die Testbarkeit zu verbessern. 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 das NavigationService hier dargestellte den Ansichtsmodelltyp als Ziel an, zu dem navigiert werden soll.

Die mobile eShopOnContainers-App verwendet die NavigationService -Klasse, um die Modell-first-Navigation anzuzeigen. Diese Klasse implementiert die INavigationService-Schnittstelle, die im folgenden Codebeispiel gezeigt wird:

public interface INavigationService  
{  
    ViewModelBase PreviousPageViewModel { get; }  
    Task InitializeAsync();  
    Task NavigateToAsync<TViewModel>() where TViewModel : ViewModelBase;  
    Task NavigateToAsync<TViewModel>(object parameter) where TViewModel : ViewModelBase;  
    Task RemoveLastFromBackStackAsync();  
    Task RemoveBackStackAsync();  
}

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 Führt eine hierarchische Navigation zu einer angegebenen Seite aus.
NavigateToAsync(parameter) Führt eine hierarchische Navigation zu einer angegebenen Seite durch und übergibt einen Parameter.
RemoveLastFromBackStackAsync Entfernt die vorherige Seite aus dem Navigationsstapel.
RemoveBackStackAsync Entfernt alle vorherigen Seiten aus dem Navigationsstapel.

Darüber hinaus gibt die INavigationService Schnittstelle an, dass eine implementierenden Klasse eine PreviousPageViewModel Eigenschaft bereitstellen muss. Diese Eigenschaft gibt den Ansichtsmodelltyp zurück, der der vorherigen Seite im Navigationsstapel zugeordnet ist.

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 mobilen eShopOnContainers-App, da sie nicht erforderlich ist.

Erstellen der NavigationService-Instanz

Die NavigationService -Klasse, die die INavigationService -Schnittstelle implementiert, wird als Singleton mit dem Autofac-Abhängigkeitseinschleusungscontainer registriert, wie im folgenden Codebeispiel veranschaulicht:

builder.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();

Die INavigationService Schnittstelle wird im ViewModelBase Klassenkonstruktor aufgelöst, wie im folgenden Codebeispiel veranschaulicht:

NavigationService = ViewModelLocator.Resolve<INavigationService>();

Dadurch wird ein Verweis auf das NavigationService Objekt zurückgegeben, das im Autofac-Abhängigkeitseinschleusungscontainer gespeichert ist, der von der InitNavigation -Methode in der App -Klasse erstellt wird. Weitere Informationen finden Sie unter Navigieren beim Starten der App.

Die ViewModelBase-Klasse speichert die NavigationService-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. Dadurch wird vermieden, dass das Objekt aus dem NavigationService Autofac-Abhängigkeitseinschleusungscontainer in jede Ansichtsmodellklasse eingefügt wird.

Behandeln von Navigationsanforderungen

Xamarin.Forms stellt die NavigationPage -Klasse bereit, die eine hierarchische Navigation implementiert, in der der Benutzer nach Bedarf durch Seiten navigieren kann, vorwärts und rückwärts. Weitere Informationen zur hierarchischen Navigation finden Sie unter Hierarchical Navigation (Hierarchische Navigation).

Anstatt die NavigationPage Klasse direkt zu verwenden, umschließt die eShopOnContainers-App die NavigationPage Klasse in der CustomNavigationView -Klasse, wie im folgenden Codebeispiel gezeigt:

public partial class CustomNavigationView : NavigationPage  
{  
    public CustomNavigationView() : base()  
    {  
        InitializeComponent();  
    }  

    public CustomNavigationView(Page root) : base(root)  
    {  
        InitializeComponent();  
    }  
}

Der Zweck dieses Wrappings besteht darin, die NavigationPage instance in der XAML-Datei für die Klasse zu formatieren.

Die Navigation erfolgt in Ansichtsmodellklassen, indem eine der NavigateToAsync Methoden aufgerufen wird, wobei der Ansichtsmodelltyp für die Seite angegeben wird, zu der navigiert wird, wie im folgenden Codebeispiel veranschaulicht:

await NavigationService.NavigateToAsync<MainViewModel>();

Das folgende Codebeispiel zeigt die NavigateToAsync von der -Klasse bereitgestellten NavigationService Methoden:

public Task NavigateToAsync<TViewModel>() where TViewModel : ViewModelBase  
{  
    return InternalNavigateToAsync(typeof(TViewModel), null);  
}  

public Task NavigateToAsync<TViewModel>(object parameter) where TViewModel : ViewModelBase  
{  
    return InternalNavigateToAsync(typeof(TViewModel), parameter);  
}

Jede Methode ermöglicht jeder Ansichtsmodellklasse, die von der -Klasse abgeleitet ist, die ViewModelBase hierarchische Navigation durch Aufrufen der InternalNavigateToAsync -Methode auszuführen. Darüber hinaus ermöglicht die zweite NavigateToAsync Methode die Angabe von Navigationsdaten als Argument, 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.

Die InternalNavigateToAsync -Methode führt die Navigationsanforderung aus und wird im folgenden Codebeispiel gezeigt:

private async Task InternalNavigateToAsync(Type viewModelType, object parameter)  
{  
    Page page = CreatePage(viewModelType, parameter);  

    if (page is LoginView)  
    {  
        Application.Current.MainPage = new CustomNavigationView(page);  
    }  
    else  
    {  
        var navigationPage = Application.Current.MainPage as CustomNavigationView;  
        if (navigationPage != null)  
        {  
            await navigationPage.PushAsync(page);  
        }  
        else  
        {  
            Application.Current.MainPage = new CustomNavigationView(page);  
        }  
    }  

    await (page.BindingContext as ViewModelBase).InitializeAsync(parameter);  
}  

private Type GetPageTypeForViewModel(Type viewModelType)  
{  
    var viewName = viewModelType.FullName.Replace("Model", string.Empty);  
    var viewModelAssemblyName = viewModelType.GetTypeInfo().Assembly.FullName;  
    var viewAssemblyName = string.Format(  
                CultureInfo.InvariantCulture, "{0}, {1}", viewName, viewModelAssemblyName);  
    var viewType = Type.GetType(viewAssemblyName);  
    return viewType;  
}  

private Page CreatePage(Type viewModelType, object parameter)  
{  
    Type pageType = GetPageTypeForViewModel(viewModelType);  
    if (pageType == null)  
    {  
        throw new Exception($"Cannot locate page type for {viewModelType}");  
    }  

    Page page = Activator.CreateInstance(pageType) as Page;  
    return page;  
}

Die InternalNavigateToAsync -Methode führt die Navigation zu einem Ansichtsmodell durch, indem zuerst die CreatePage -Methode aufgerufen wird. Diese Methode sucht die Sicht, die dem angegebenen Ansichtsmodelltyp entspricht, und erstellt und gibt einen instance dieses Ansichtstyps zurück. Beim Suchen der Sicht, die dem Ansichtsmodelltyp entspricht, wird ein konventionsbasierter Ansatz verwendet, bei dem folgendes vorausgesetzt wird:

  • Ansichten befinden sich in derselben Assembly wie Ansichtsmodelltypen.
  • Ansichten befinden sich in einem . Der untergeordnete Namespace wird angezeigt.
  • Ansichtsmodelle befinden sich in einem . Untergeordneter ViewModels-Namespace.
  • Ansichtsnamen entsprechen Ansichtsmodellnamen, wobei "Modell" entfernt wurde.

Wenn eine Ansicht instanziiert wird, wird sie ihrem entsprechenden Ansichtsmodell zugeordnet. Weitere Informationen dazu finden Sie unter Automatisches Erstellen eines Ansichtsmodells mit einem Ansichtsmodell-Locator.

Wenn die zu erstellende Ansicht ein LoginViewist, wird sie in eine neue instance der CustomNavigationView -Klasse eingeschlossen und der Application.Current.MainPage -Eigenschaft zugewiesen. Andernfalls wird die CustomNavigationView instance abgerufen, und vorausgesetzt, sie ist nicht NULL, wird die PushAsync -Methode aufgerufen, um die zu erstellende Ansicht auf den Navigationsstapel zu pushen. Wenn jedoch der abgerufene CustomNavigationView instance istnull, wird die zu erstellende Ansicht in eine neue instance der CustomNavigationView -Klasse eingeschlossen und der Application.Current.MainPage -Eigenschaft zugewiesen. Dieser Mechanismus stellt sicher, dass seiten während der Navigation ordnungsgemäß dem Navigationsstapel hinzugefügt werden, wenn er leer ist und wenn er Daten enthält.

Tipp

Erwägen Sie das Zwischenspeichern von Seiten. Seitenzwischenspeicherung führt zu Arbeitsspeicherverbrauch für Ansichten, die derzeit nicht angezeigt werden. Ohne Seitenzwischenspeicherung bedeutet dies jedoch, dass die XAML-Analyse und -Konstruktion der Seite und des zugehörigen Ansichtsmodells jedes Mal erfolgen, wenn eine neue Seite navigiert wird, was sich auf die Leistung einer komplexen Seite auswirken kann. Für eine gut gestaltete Seite, die keine übermäßige Anzahl von Steuerelementen verwendet, sollte die Leistung ausreichend sein. Das Zwischenspeichern von Seiten kann jedoch hilfreich sein, wenn langsame Seitenladezeiten auftreten.

Nachdem die Ansicht erstellt und zu navigiert wurde, wird die InitializeAsync Methode des zugeordneten Ansichtsmodells der Ansicht ausgeführt. Weitere Informationen finden Sie unter Übergeben von Parametern während der Navigation.

Wenn die App gestartet wird, wird die InitNavigation -Methode in der App -Klasse aufgerufen. Im folgenden Codebeispiel wird diese Methode veranschaulicht:

private Task InitNavigation()  
{  
    var navigationService = ViewModelLocator.Resolve<INavigationService>();  
    return navigationService.InitializeAsync();  
}

Die -Methode erstellt ein neues NavigationService Objekt im Autofac-Abhängigkeitseinschleusungscontainer und gibt einen Verweis darauf zurück, bevor die InitializeAsync -Methode aufgerufen wird.

Hinweis

Wenn die INavigationService Schnittstelle von der ViewModelBase -Klasse aufgelöst wird, gibt der Container einen Verweis auf das Objekt zurück, das NavigationService beim Aufrufen der InitNavigation-Methode erstellt wurde.

Das folgende Codebeispiel zeigt die NavigationServiceInitializeAsync -Methode:

public Task InitializeAsync()  
{  
    if (string.IsNullOrEmpty(Settings.AuthAccessToken))  
        return NavigateToAsync<LoginViewModel>();  
    else  
        return NavigateToAsync<MainViewModel>();  
}

Wird MainView zu navigiert, wenn die App über ein zwischengespeichertes Zugriffstoken verfügt, das für die Authentifizierung verwendet wird. Andernfalls wird zu LoginView navigiert.

Weitere Informationen zum Autofac-Container für abhängigkeitsinjektion finden Sie unter Einführung in die Abhängigkeitsinjektion.

Übergeben von Parametern während der Navigation

Eine der NavigateToAsync Methoden, die von der INavigationService -Schnittstelle angegeben wird, ermöglicht die Angabe von Navigationsdaten als Argument, das an das Ansichtsmodell übergeben wird, zu dem navigiert wird, wo es normalerweise zum Durchfü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)  
{  
    await NavigationService.NavigateToAsync<OrderDetailViewModel>(order);  
}

Diese Methode ruft die OrderDetailViewModelNavigation zum auf, und übergibt eine Order instance, die die Reihenfolge darstellt, die der Benutzer auf der ProfileView Seite ausgewählt hat. Wenn die NavigationService -Klasse erstellt OrderDetailView, wird die OrderDetailViewModel -Klasse instanziiert und der -Ansicht BindingContextzugewiesen. Nach dem Navigieren zu OrderDetailViewführt die InternalNavigateToAsync -Methode die InitializeAsync -Methode des zugeordneten Ansichtsmodells aus.

Die InitializeAsync -Methode wird in der ViewModelBase -Klasse als Methode definiert, die überschrieben werden kann. Diese Methode gibt ein object Argument an, das die Daten darstellt, die während eines Navigationsvorgangs an ein Ansichtsmodell übergeben werden sollen. Daher stellen Ansichtsmodellklassen, die Daten von einem Navigationsvorgang empfangen möchten, eine eigene Implementierung der InitializeAsync -Methode bereit, um die erforderliche Initialisierung durchzuführen. Im folgenden Codebeispiel wird die InitializeAsync-Methode aus der OrderDetailViewModel-Klasse gezeigt:

public override async Task InitializeAsync(object navigationData)  
{  
    if (navigationData is Order)  
    {  
        ...  
        Order = await _ordersService.GetOrderAsync(  
                        Convert.ToInt32(order.OrderNumber), authToken);  
        ...  
    }  
}

Diese Methode ruft die Order instance ab, die während des Navigationsvorgangs an das Ansichtsmodell übergeben wurde, und verwendet sie, um die vollständigen Bestelldetails aus dem OrderService instance abzurufen.

Aufrufen der Navigation mithilfe von Verhaltensweisen

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 zu WebView einer Webseite navigiert, wird das Navigating -Ereignis ausgelöst, das den NavigateCommand im LoginViewModelausfü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 Actionübergeben.

Dadurch führt NavigationCommand wiederum die NavigateAsync-Methode aus, wie im folgenden Codebeispiel gezeigt:

private async Task NavigateAsync(string url)  
{  
    ...          
    await NavigationService.NavigateToAsync<MainViewModel>();  
    await NavigationService.RemoveLastFromBackStackAsync();  
    ...  
}

Diese Methode ruft die MainViewModelNavigation zum auf, und die folgende Navigation entfernt die LoginView Seite aus dem Navigationsstapel.

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 aus einer Benachrichtigung verwendet wird, um zu steuern, ob die Navigation aufgerufen wird oder nicht.

Zusammenfassung

Xamarin.Forms bietet Unterstützung für die 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. 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 Ansichtsmodell-first-Navigation aus Ansichtsmodellen auszuführen. Das Platzieren von Navigationslogik in Ansichtsmodellklassen bedeutet, dass die Logik über 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.