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.
Nawigowanie między stronami
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ę NavigationPage
CustomNavigationView
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.
Nawigowanie po uruchomieniu aplikacji
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ę NavigationService
InitializeAsync
:
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 OrderDetailViewModel
obiektu , 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 BindingContext
do widoku . Po przejściu OrderDetailView
InternalNavigateToAsync
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ę InitializeAsync
OrderDetailViewModel
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 Action
elementu .
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 MainViewModel
elementu 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.