Udostępnij za pośrednictwem


Nawigacja aplikacji dla przedsiębiorstw

Uwaga

Ta książka elektroniczna została opublikowana wiosną 2017 r. i od tego czasu nie została zaktualizowana. Jest wiele w książce, która pozostaje cenna, ale niektóre z materiałów są przestarzałe.

Xamarin.Forms Obejmuje obsługę nawigacji między stronami, która zazwyczaj wynika z interakcji użytkownika z interfejsem użytkownika lub z samej aplikacji w wyniku wewnętrznych zmian stanu logiki. Jednak nawigacja może być złożona w celu zaimplementowania w aplikacjach korzystających ze wzorca Model-View-ViewModel (MVVM), ponieważ muszą zostać spełnione następujące wyzwania:

  • Sposób identyfikowania widoku, do którego ma być przechodziny, przy użyciu podejścia, które nie wprowadza ścisłego sprzęgania i zależności między widokami.
  • Sposób koordynowania procesu, za pomocą którego widok ma zostać przeniesiony, jest tworzone i inicjowane. W przypadku korzystania z maszyny MVVM model widoku i widoku należy utworzyć wystąpienie i skojarzyć ze sobą za pośrednictwem kontekstu powiązania widoku. Gdy aplikacja korzysta z kontenera iniekcji zależności, utworzenie wystąpienia widoków i modeli wyświetlania może wymagać określonego mechanizmu budowy.
  • Niezależnie od tego, czy należy wykonywać nawigację typu "pierwszy widok", czy też wyświetlać nawigację po pierwszym modelu. W przypadku nawigacji po pierwszym widoku strona do przejścia odwołuje się do nazwy typu widoku. Podczas nawigacji określony widok jest tworzone razem z odpowiednim modelem widoku i innymi usługami zależnymi. Alternatywną metodą jest użycie nawigacji typu "pierwszy model", w której strona do przejścia odwołuje się do nazwy typu modelu widoku.
  • Sposób czystego oddzielenia zachowania nawigacji aplikacji między widokami i modelami wyświetlania. Wzorzec MVVM zapewnia separację między interfejsem użytkownika aplikacji a jego prezentacją i logiką biznesową. Jednak zachowanie nawigacji aplikacji często obejmuje interfejs użytkownika i części prezentacji aplikacji. Użytkownik często inicjuje nawigację z widoku, a widok zostanie zastąpiony w wyniku nawigacji. Jednak nawigacja może być często konieczna lub skoordynowana z poziomu modelu widoku.
  • Sposób przekazywania parametrów podczas nawigacji na potrzeby inicjowania. Jeśli na przykład użytkownik przejdzie do widoku w celu zaktualizowania szczegółów zamówienia, dane zamówienia będą musiały zostać przekazane do widoku, aby można było wyświetlić poprawne dane.
  • Sposób koordynowania nawigacji w celu zapewnienia przestrzegania określonych reguł biznesowych. Na przykład użytkownicy mogą być monitowani przed przejściem z widoku, aby poprawić wszelkie nieprawidłowe dane lub poprosić o przesłanie lub odrzucenie wszelkich zmian danych wprowadzonych w widoku.

W tym rozdziale przedstawiono te wyzwania, przedstawiając klasę używaną NavigationService do wyświetlania nawigacji po pierwszej stronie modelu.

Uwaga

Używana NavigationService przez aplikację jest przeznaczona tylko do wykonywania hierarchicznej nawigacji między wystąpieniami programu ContentPage. Przechodzenie między innymi typami stron przy użyciu usługi może spowodować nieoczekiwane zachowanie.

Logika nawigacji może znajdować się w kodzie widoku lub w modelu widoku powiązanego z danymi. Umieszczenie logiki nawigacji w widoku może być najprostszym podejściem, ale nie jest łatwo testowalne za pomocą testów jednostkowych. Umieszczenie logiki nawigacji w klasach modelu widoków oznacza, że logikę można wykonywać za pomocą testów jednostkowych. Ponadto model widoków może następnie zaimplementować logikę w celu kontrolowania nawigacji w celu zapewnienia, że niektóre reguły biznesowe są wymuszane. Na przykład aplikacja może nie zezwalać użytkownikowi na odejście od strony bez uprzedniego upewnienia się, że wprowadzone dane są prawidłowe.

Klasa NavigationService jest zwykle wywoływana z modeli widoków w celu podwyższenia poziomu możliwości testowania. Jednak przejście do widoków z modeli widoków wymagałoby, aby modele widoku odwoływały się do widoków, a szczególnie widoków, z którymi nie jest skojarzony aktywny model widoku, co nie jest zalecane. W związku z NavigationService tym przedstawiony tutaj określa typ modelu widoku jako element docelowy do przejścia.

Aplikacja mobilna eShopOnContainers używa klasy w celu zapewnienia nawigacji opartej NavigationService na modelu. Ta klasa implementuje INavigationService interfejs pokazany w poniższym przykładzie kodu:

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();  
}

Ten interfejs określa, że klasa implementowania musi zapewnić następujące metody:

Metoda Purpose
InitializeAsync Wykonuje nawigację do jednej z dwóch stron po uruchomieniu aplikacji.
NavigateToAsync Wykonuje nawigację hierarchiczną do określonej strony.
NavigateToAsync(parameter) Wykonuje hierarchiczną nawigację do określonej strony, przekazując parametr.
RemoveLastFromBackStackAsync Usuwa poprzednią stronę ze stosu nawigacji.
RemoveBackStackAsync Usuwa wszystkie poprzednie strony ze stosu nawigacji.

Ponadto interfejs określa, INavigationService że klasa implementowania musi podać PreviousPageViewModel właściwość. Ta właściwość zwraca typ modelu widoku skojarzony z poprzednią stroną w stosie nawigacji.

Uwaga

Interfejs INavigationService zazwyczaj określa również metodę GoBackAsync , która jest używana do programowego powrotu do poprzedniej strony w stosie nawigacji. Jednak ta metoda nie jest dostępna w aplikacji mobilnej eShopOnContainers, ponieważ nie jest wymagana.

Tworzenie wystąpienia usługi NavigationService

Klasa NavigationService , która implementuje INavigationService interfejs, jest rejestrowana jako pojedynczy kontener iniekcji zależności autofac, jak pokazano w poniższym przykładzie kodu:

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

Interfejs INavigationService jest rozpoznawany w konstruktorze ViewModelBase klasy, jak pokazano w poniższym przykładzie kodu:

NavigationService = ViewModelLocator.Resolve<INavigationService>();

Spowoduje to zwrócenie odwołania do NavigationService obiektu przechowywanego w kontenerze iniekcji zależności Autofac, który jest tworzony przez InitNavigation metodę w App klasie . Aby uzyskać więcej informacji, zobacz Nawigowanie po uruchomieniu aplikacji.

Klasa ViewModelBase przechowuje NavigationService wystąpienie we NavigationService właściwości typu INavigationService. W związku z tym wszystkie klasy modelu wyświetlania, które pochodzą z ViewModelBase klasy, mogą używać NavigationService właściwości w celu uzyskania dostępu do metod określonych przez INavigationService interfejs. Pozwala to uniknąć narzutu na wstrzyknięcie NavigationService obiektu z kontenera wstrzykiwania zależności Autofac do każdej klasy modelu widoku.

Obsługa żądań nawigacji

Xamarin.Forms Udostępnia klasę NavigationPage , która implementuje hierarchiczne środowisko nawigacji, w którym użytkownik może poruszać się po stronach, do przodu i do tyłu zgodnie z potrzebami. Aby uzyskać więcej informacji na temat nawigacji hierarchicznej, zobacz Nawigacja hierarchiczna.

Zamiast bezpośrednio używać NavigationPage klasy, aplikacja eShopOnContainers opakowuje klasę NavigationPageCustomNavigationView w klasie, jak pokazano w poniższym przykładzie kodu:

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

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

Celem tego zawijania jest łatwość stylizowania NavigationPage wystąpienia wewnątrz pliku XAML dla klasy.

Nawigacja jest wykonywana wewnątrz klas modelu widoku, wywołując jedną z NavigateToAsync metod, określając typ modelu widoku dla strony, do której jest przechodzina, jak pokazano w poniższym przykładzie kodu:

await NavigationService.NavigateToAsync<MainViewModel>();

Poniższy przykład kodu przedstawia NavigateToAsync metody udostępniane przez klasę NavigationService :

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);  
}

Każda metoda umożliwia dowolną klasę modelu widoku, która pochodzi z ViewModelBase klasy do wykonywania nawigacji hierarchicznej przez wywołanie InternalNavigateToAsync metody. Ponadto druga NavigateToAsync metoda umożliwia określenie danych nawigacji jako argumentu przekazywanego do modelu widoku, do którego zwykle jest używana do inicjowania. Aby uzyskać więcej informacji, zobacz Przekazywanie parametrów podczas nawigacji.

Metoda InternalNavigateToAsync wykonuje żądanie nawigacji i jest wyświetlana w poniższym przykładzie kodu:

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;  
}

Metoda InternalNavigateToAsync wykonuje nawigację do modelu widoku, wywołując najpierw metodę CreatePage . Ta metoda lokalizuje widok odpowiadający określonemu typowi modelu widoku i tworzy i zwraca wystąpienie tego typu widoku. Lokalizowanie widoku odpowiadającego typowi modelu widoku korzysta z podejścia opartego na konwencji, które zakłada, że:

  • Widoki znajdują się w tym samym zestawie co typy modelu widoku.
  • Widoki znajdują się w obiekcie . Wyświetla podrzędną przestrzeń nazw.
  • Modele wyświetlania znajdują się w elemecie . Przestrzeń nazw podrzędnych modelu ViewModels.
  • Nazwy widoków odpowiadają nazwam modelu z usuniętym ciągiem "Model".

Po utworzeniu wystąpienia widoku jest on skojarzony z odpowiednim modelem widoku. Aby uzyskać więcej informacji na temat tego, jak to się dzieje, zobacz Automatyczne tworzenie modelu widoku za pomocą lokalizatora modelu widoku.

Jeśli tworzony widok jest elementem LoginView, jest on opakowany wewnątrz nowego wystąpienia CustomNavigationView klasy i przypisany do Application.Current.MainPage właściwości . CustomNavigationView W przeciwnym razie wystąpienie jest pobierane i pod warunkiem, że nie ma wartości null, PushAsync metoda jest wywoływana w celu wypchnięcia widoku tworzonego do stosu nawigacji. Jeśli jednak pobrane CustomNavigationView wystąpienie to null, widok tworzony jest owinięty wewnątrz nowego wystąpienia CustomNavigationView klasy i przypisany do Application.Current.MainPage właściwości . Ten mechanizm zapewnia, że podczas nawigacji strony są dodawane poprawnie do stosu nawigacji zarówno wtedy, gdy są puste, jak i gdy zawierają dane.

Napiwek

Rozważ buforowanie stron. Buforowanie stron powoduje użycie pamięci dla widoków, które nie są obecnie wyświetlane. Jednak bez buforowania stron oznacza to, że analizowanie i konstruowanie strony XAML oraz jej model wyświetlania wystąpi za każdym razem, gdy zostanie wyświetlona nowa strona, co może mieć wpływ na wydajność złożonej strony. W przypadku dobrze zaprojektowanej strony, która nie używa nadmiernej liczby kontrolek, wydajność powinna być wystarczająca. Jednak buforowanie stron może pomóc w przypadku napotkania wolnych czasów ładowania stron.

Po utworzeniu widoku i przejściu do InitializeAsync niej zostanie wykonana metoda skojarzonego modelu widoku. Aby uzyskać więcej informacji, zobacz Przekazywanie parametrów podczas nawigacji.

Po uruchomieniu aplikacji wywoływana InitNavigation jest metoda w App klasie . Poniższy przykład kodu przedstawia tę metodę:

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

Metoda tworzy nowy NavigationService obiekt w kontenerze iniekcji zależności autofac i zwraca do niego odwołanie przed wywołaniem metody InitializeAsync .

Uwaga

INavigationService Gdy interfejs jest rozpoznawany przez ViewModelBase klasę, kontener zwraca odwołanie do obiektu utworzonego NavigationService podczas wywoływanej metody InitNavigation.

Poniższy przykład kodu przedstawia metodę NavigationServiceInitializeAsync :

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

Zostanie MainView on przekazany do adresu , jeśli aplikacja ma buforowany token dostępu, który jest używany do uwierzytelniania. LoginView W przeciwnym razie nastąpi przejście do elementu .

Aby uzyskać więcej informacji na temat kontenera wstrzykiwania zależności autofak, zobacz Wprowadzenie do wstrzykiwania zależności.

Przekazywanie parametrów podczas nawigacji

NavigateToAsync Jedną z metod określonych przez INavigationService interfejs umożliwia określenie danych nawigacji jako argumentu przekazywanego do modelu widoku, do którego jest zwykle używany do inicjowania.

Na przykład ProfileViewModel klasa zawiera element OrderDetailCommand wykonywany, gdy użytkownik wybierze zamówienie na ProfileView stronie. Z kolei spowoduje to wykonanie OrderDetailAsync metody, która jest pokazana w poniższym przykładzie kodu:

private async Task OrderDetailAsync(Order order)  
{  
    await NavigationService.NavigateToAsync<OrderDetailViewModel>(order);  
}

Ta metoda wywołuje nawigację do OrderDetailViewModelobiektu , przekazując wystąpienie reprezentujące Order kolejność wybraną przez użytkownika na ProfileView stronie. NavigationService Gdy klasa tworzy klasę OrderDetailView, OrderDetailViewModel zostanie utworzona wystąpienie klasy i przypisana BindingContextdo widoku . Po przejściu OrderDetailViewInternalNavigateToAsync do metody metoda wykonuje metodę InitializeAsync skojarzonego modelu widoku widoku.

Metoda InitializeAsync jest definiowana ViewModelBase w klasie jako metoda, którą można zastąpić. Ta metoda określa object argument reprezentujący dane, które mają być przekazywane do modelu widoku podczas operacji nawigacji. W związku z tym wyświetl klasy modeli, które chcą odbierać dane z operacji nawigacji, zapewniają własną implementację InitializeAsync metody w celu wykonania wymaganej inicjalizacji. Poniższy przykład kodu przedstawia metodę InitializeAsyncOrderDetailViewModel z klasy :

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

Ta metoda pobiera Order wystąpienie przekazane do modelu widoku podczas operacji nawigacji i używa go do pobrania pełnych szczegółów zamówienia z OrderService wystąpienia.

Wywoływanie nawigacji przy użyciu zachowań

Nawigacja jest zwykle wyzwalana z widoku przez interakcję użytkownika. Na przykład funkcja LoginView wykonuje nawigację po pomyślnym uwierzytelnieniu. Poniższy przykład kodu pokazuje, jak nawigacja jest wywoływana przez zachowanie:

<WebView ...>  
    <WebView.Behaviors>  
        <behaviors:EventToCommandBehavior  
            EventName="Navigating"  
            EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"  
            Command="{Binding NavigateCommand}" />  
    </WebView.Behaviors>  
</WebView>

W czasie wykonywania obiekt EventToCommandBehavior będzie reagować na interakcję z elementem WebView. Po przejściu WebView do strony Navigating internetowej zdarzenie zostanie wyzwolony, co spowoduje wykonanie elementu NavigateCommand w pliku LoginViewModel. Domyślnie argumenty zdarzeń dla zdarzenia są przekazywane do polecenia . Te dane są konwertowane, ponieważ są przekazywane między źródłem i obiektem docelowym przez konwerter określony we EventArgsConverter właściwości , która zwraca wartość Url z WebNavigatingEventArgs. W związku z tym po wykonaniu NavigationCommand adresu URL strony internetowej jest przekazywany jako parametr do zarejestrowanego Actionelementu .

Z kolei NavigationCommand metoda wykonuje metodę NavigateAsync , która jest wyświetlana w poniższym przykładzie kodu:

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

Ta metoda wywołuje nawigację do MainViewModelelementu i po nawigacji usuwa LoginView stronę ze stosu nawigacji.

Potwierdzanie lub anulowanie nawigacji

Aplikacja może wymagać interakcji z użytkownikiem podczas operacji nawigacji, aby użytkownik mógł potwierdzić lub anulować nawigację. Może to być konieczne, na przykład gdy użytkownik spróbuje przejść przed pełnym ukończeniem strony wprowadzania danych. W takiej sytuacji aplikacja powinna podać powiadomienie, które umożliwia użytkownikowi przejście z dala od strony lub anulowanie operacji nawigacji przed jej wystąpieniem. Można to osiągnąć w klasie modelu widoku przy użyciu odpowiedzi z powiadomienia w celu kontrolowania, czy jest wywoływana nawigacja.

Podsumowanie

Xamarin.Forms Obejmuje obsługę nawigacji po stronie, która zazwyczaj wynika z interakcji użytkownika z interfejsem użytkownika lub z samej aplikacji w wyniku zmian stanu opartego na logice wewnętrznej. Jednak nawigacja może być złożona do zaimplementowania w aplikacjach korzystających ze wzorca MVVM.

W tym rozdziale przedstawiono klasę, która służy do wyświetlania nawigacji opartej NavigationService na modelu z poziomu modeli widoków. Umieszczenie logiki nawigacji w klasach modelu widoku oznacza, że logikę można wykonywać za pomocą testów automatycznych. Ponadto model widoków może następnie zaimplementować logikę w celu kontrolowania nawigacji w celu zapewnienia, że niektóre reguły biznesowe są wymuszane.