Sdílet prostřednictvím


Navigace v podnikových aplikacích

Poznámka:

Tato elektronická kniha byla publikována na jaře roku 2017 a od té doby nebyla aktualizována. Existuje mnoho v knize, která zůstává cenná, ale některé materiály jsou zastaralé.

Xamarin.Forms zahrnuje podporu navigace na stránce, která obvykle vede k interakci uživatele s uživatelským rozhraním nebo samotnou aplikací v důsledku interních změn stavu řízeného logikou. Navigace ale může být složitá pro implementaci v aplikacích, které používají model model-View-ViewModel (MVVM), protože musí být splněny následující výzvy:

  • Jak identifikovat zobrazení, na které se má přejít, pomocí přístupu, který nezavádí úzkou vazbu a závislosti mezi zobrazeními.
  • Jak koordinovat proces, na který se má zobrazení přejít, vytvoří instanci a inicializuje se. Při použití MVVM se model zobrazení a zobrazení musí vytvořit instanci a přidružit k sobě prostřednictvím kontextu vazby zobrazení. Když aplikace používá kontejner injektáž závislostí, vytvoření instance zobrazení a modelů zobrazení může vyžadovat konkrétní stavební mechanismus.
  • Určuje, jestli se má provést navigace typu první zobrazení, nebo zobrazit navigaci typu model-first. Při první navigaci zobrazení se stránka, na které chcete přejít, odkazuje na název typu zobrazení. Během navigace se vytvoří instance zadaného zobrazení spolu s odpovídajícím modelem zobrazení a dalšími závislými službami. Alternativním přístupem je použít navigaci typu model-first, kde stránka, na které chcete přejít, odkazuje na název typu modelu zobrazení.
  • Jak vyčistit navigační chování aplikace napříč zobrazeními a modely zobrazení. Model MVVM poskytuje oddělení mezi uživatelským rozhraním aplikace a jeho prezentací a obchodní logikou. Chování aplikace při navigaci ale často pokrývá části uživatelského rozhraní a prezentací aplikace. Uživatel bude často inicializovat navigaci ze zobrazení a zobrazení bude nahrazeno v důsledku navigace. Navigace ale může být často potřeba iniciovat nebo koordinovat také z modelu zobrazení.
  • Jak předat parametry během navigace pro inicializační účely. Pokud například uživatel přejde do zobrazení pro aktualizaci podrobností objednávky, budou se data objednávky muset předat do zobrazení, aby mohla zobrazit správná data.
  • Jak koordinovat navigaci, aby byla zajištěna dodržování určitých obchodních pravidel. Uživatelům se například může zobrazit výzva před přechodem z zobrazení, aby mohli opravit všechna neplatná data nebo být vyzváni k odeslání nebo zahození jakýchkoli změn dat provedených v zobrazení.

Tato kapitola řeší tyto výzvy tím, že prezentuje NavigationService třídu, která se používá k zobrazení navigace na první stránce modelu.

Poznámka:

Aplikace NavigationService je určena pouze k provádění hierarchické navigace mezi instancemi ContentPage. Použití služby k navigaci mezi jinými typy stránek může vést k neočekávanému chování.

Navigační logika se může nacházet v kódu zobrazení nebo v modelu vázaného zobrazení dat. I když umístění navigační logiky do zobrazení může být nejjednodušším přístupem, není snadné ji testovat pomocí testů jednotek. Umístění navigační logiky do tříd modelu zobrazení znamená, že logiku je možné provádět pomocí testů jednotek. Model zobrazení pak může implementovat logiku pro řízení navigace, aby se zajistilo, že se vynucují určitá obchodní pravidla. Aplikace například nemusí uživateli povolit navigaci mimo stránku, aniž by nejprve zajistila platnost zadaných dat.

Třída NavigationService je obvykle vyvolána z modelů zobrazení za účelem zvýšení testovatelnosti. Přechod na zobrazení z modelů zobrazení ale vyžaduje, aby modely zobrazení odkazovaly na zobrazení, a zejména zobrazení, ke kterým není přidružený aktivní model zobrazení, což se nedoporučuje. Proto uvedený typ NavigationService modelu zobrazení určuje jako cíl, na který se má přejít.

Mobilní aplikace eShopOnContainers používá NavigationService třídu k poskytnutí první navigace modelu. Tato třída implementuje INavigationService rozhraní, které je znázorněno v následujícím příkladu kódu:

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

Toto rozhraní určuje, že implementovat třídu musí poskytovat následující metody:

Způsob Účel
InitializeAsync Při spuštění aplikace provede navigaci na jednu ze dvou stránek.
NavigateToAsync Provede hierarchickou navigaci na zadanou stránku.
NavigateToAsync(parameter) Provede hierarchickou navigaci na zadanou stránku a předá parametr.
RemoveLastFromBackStackAsync Odebere předchozí stránku z navigačního zásobníku.
RemoveBackStackAsync Odebere všechny předchozí stránky z navigačního zásobníku.

Kromě toho rozhraní určuje, INavigationService že implementovat třídu musí poskytovat PreviousPageViewModel vlastnost. Tato vlastnost vrátí typ modelu zobrazení přidružený k předchozí stránce v navigačním zásobníku.

Poznámka:

Rozhraní INavigationService obvykle také určuje metodu GoBackAsync , která se používá k programovému návratu na předchozí stránku v navigačním zásobníku. Tato metoda však v mobilní aplikaci eShopOnContainers chybí, protože není nutná.

Vytvoření instance navigationservice

Třída NavigationService , která implementuje INavigationService rozhraní, je registrována jako singleton s kontejnerem injektáže závislostí Autofac, jak je znázorněno v následujícím příkladu kódu:

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

Rozhraní INavigationService je vyřešeno v konstruktoru ViewModelBase třídy, jak je znázorněno v následujícím příkladu kódu:

NavigationService = ViewModelLocator.Resolve<INavigationService>();

Vrátí odkaz na NavigationService objekt uložený v kontejneru injektáže závislostí Autofac, který je vytvořen metodou InitNavigation ve App třídě. Další informace najdete v tématu Navigace při spuštění aplikace.

Třída ViewModelBase ukládá NavigationService instanci do NavigationService vlastnosti typu INavigationService. Proto všechny třídy modelu zobrazení, které jsou odvozeny z ViewModelBase třídy, mohou použít NavigationService vlastnost pro přístup k metodám určeným rozhraním INavigationService . Tím se zabrání režii při vkládání objektu NavigationService z kontejneru injektáže závislostí Autofac do každé třídy modelu zobrazení.

Zpracování žádostí o navigaci

Xamarin.FormsNavigationPage poskytuje třídu, která implementuje hierarchické navigační prostředí, ve kterém uživatel může podle potřeby procházet stránky, dopředu a dozadu. Další informace o hierarchické navigaci naleznete v tématu Hierarchická navigace.

Místo přímého použití NavigationPage třídy eShopOnContainers aplikace zabalí NavigationPage třídu do CustomNavigationView třídy, jak je znázorněno v následujícím příkladu kódu:

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

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

Účelem tohoto zabalení je snadné stylování NavigationPage instance uvnitř souboru XAML pro třídu.

Navigace se provádí uvnitř tříd modelu zobrazení vyvoláním jedné z NavigateToAsync metod a určením typu modelu zobrazení pro stránku, na kterou se přechází, jak je znázorněno v následujícím příkladu kódu:

await NavigationService.NavigateToAsync<MainViewModel>();

Následující příklad kódu ukazuje NavigateToAsync metody poskytované NavigationService třídou:

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ždá metoda umožňuje jakoukoli třídu modelu zobrazení, která je odvozena od ViewModelBase třídy, provádět hierarchickou navigaci vyvoláním InternalNavigateToAsync metody. Druhá NavigateToAsync metoda navíc umožňuje zadat navigační data jako argument předaný modelu zobrazení, do kterého se přechází, kde se obvykle používá k provedení inicializace. Další informace naleznete v tématu Předávání parametrů během navigace.

Metoda InternalNavigateToAsync spustí požadavek navigace a je znázorněn v následujícím příkladu kódu:

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 provádí navigaci na model zobrazení tím, že nejprve zavolá metodu CreatePage . Tato metoda vyhledá zobrazení, které odpovídá zadanému typu modelu zobrazení, a vytvoří a vrátí instanci tohoto typu zobrazení. Umístění zobrazení odpovídající typu modelu zobrazení používá konvenční přístup, který předpokládá, že:

  • Zobrazení jsou ve stejném sestavení jako typy modelů zobrazení.
  • Zobrazení jsou v . Zobrazení podřízeného oboru názvů
  • Zobrazit modely jsou v . Podřízený obor názvů ViewModels
  • Názvy zobrazení odpovídají názvům modelů s odebraným textem Model.

Když se vytvoří instance zobrazení, je přidruženo k příslušnému modelu zobrazení. Další informace o tom, jak k tomu dochází, naleznete v tématu Automatické vytvoření modelu zobrazení pomocí lokátoru modelu zobrazení.

Pokud je LoginViewvytvořené zobrazení , je zabaleno uvnitř nové instance CustomNavigationView třídy a přiřazeno Application.Current.MainPage k vlastnosti. CustomNavigationView V opačném případě se instance načte a za předpokladu, že není null, PushAsync metoda je vyvolána pro odeslání zobrazení vytvořeného do navigačního zásobníku. Pokud je však načtená CustomNavigationView instance , je vytvořené zobrazení zabaleno uvnitř nové instance CustomNavigationView třídy a přiřazeno k Application.Current.MainPage vlastnosti.null Tento mechanismus zajišťuje, že během navigace se stránky správně přidají do navigačního zásobníku, když je prázdný a kdy obsahuje data.

Tip

Zvažte ukládání stránek do mezipaměti. Ukládání stránek do mezipaměti vede ke spotřebě paměti pro zobrazení, která se aktuálně nezobrazují. Bez ukládání stránky do mezipaměti ale znamená, že analýza a konstrukce stránky a jeho modelu zobrazení se projeví při každém přechodu na novou stránku, což může mít dopad na výkon pro složitou stránku. Pro dobře navrženou stránku, která nepoužívá nadměrný počet ovládacích prvků, by měl být výkon dostatečný. Ukládání stránek do mezipaměti ale může pomoct v případě, že dojde k pomalému načítání stránek.

Po vytvoření a přechodu na InitializeAsync zobrazení se spustí metoda přidruženého modelu zobrazení. Další informace naleznete v tématu Předávání parametrů během navigace.

Při spuštění InitNavigation aplikace je vyvolána metoda ve App třídě. Následující příklad kódu ukazuje tuto metodu:

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

Metoda vytvoří nový NavigationService objekt v kontejneru injektáže závislostí Autofac a před vyvoláním metody vrátí odkaz na něj InitializeAsync .

Poznámka:

INavigationService Když rozhraní je vyřešeno ViewModelBase třídou, kontejner vrátí odkaz na NavigationService objekt, který byl vytvořen při InitNavigation metoda je vyvolána.

Následující příklad kódu ukazuje metodu NavigationService InitializeAsync :

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

Přejde MainView se na to, jestli má aplikace přístupový token uložený v mezipaměti, který se používá k ověřování. V opačném případě se LoginView na něj přejde.

Další informace o kontejneru injektáže závislostí autofac naleznete v tématu Úvod do injektáže závislostí.

Předávání parametrů během navigace

Jedna z NavigateToAsync metod určených INavigationService rozhraním umožňuje zadat navigační data jako argument předaný modelu zobrazení, do kterého se přechází, kde se obvykle používá k provedení inicializace.

Třída například ProfileViewModel obsahuje spuštěnou OrderDetailCommand třídu, když uživatel vybere objednávku na ProfileView stránce. Následně se spustí OrderDetailAsync metoda, která je zobrazena v následujícím příkladu kódu:

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

Tato metoda vyvolá navigaci na OrderDetailViewModel, předávání Order instance, která představuje pořadí, které uživatel vybral na ProfileView stránce. NavigationService Když třída vytvoří OrderDetailView, OrderDetailViewModel třída je vytvořena a přiřazena k zobrazení BindingContext. Po přechodu na metodu OrderDetailViewInternalNavigateToAsync spustí InitializeAsync metodu přidruženého modelu zobrazení zobrazení.

Metoda InitializeAsync je definována ve ViewModelBase třídě jako metoda, která lze přepsat. Tato metoda určuje object argument, který představuje data, která se mají předat modelu zobrazení během operace navigace. Proto si prohlédněte třídy modelu, které chtějí přijímat data z navigační operace, poskytují vlastní implementaci InitializeAsync metody k provedení požadované inicializace. Následující příklad kódu ukazuje metodu InitializeAsync OrderDetailViewModel z třídy:

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

Tato metoda načte Order instanci, která byla předána do modelu zobrazení během operace navigace, a používá ji k načtení úplných podrobností objednávky z OrderService instance.

Vyvolání navigace pomocí chování

Navigace se obvykle aktivuje ze zobrazení interakcí uživatele. Například provede LoginView navigaci po úspěšném ověření. Následující příklad kódu ukazuje, jak je navigace vyvolána chováním:

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

Za běhu EventToCommandBehavior bude reagovat na interakci s .WebView WebView Když přejdete na webovou stránku, Navigating aktivuje se událost, která spustí NavigateCommand v souboru LoginViewModel. Ve výchozím nastavení se argumenty události události předávají příkazu. Tato data se převedou tak, jak se předávají mezi zdrojem a cílem převaděčem zadaným ve EventArgsConverter vlastnosti, která vrací Url z WebNavigatingEventArgs. Proto při NavigationCommand spuštění je adresa URL webové stránky předána jako parametr registrovanému Action.

Následně provede NavigationCommand metodu NavigateAsync , která je zobrazena v následujícím příkladu kódu:

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

Tato metoda vyvolá navigaci na MainViewModela následující navigaci odebere LoginView stránku z navigačního zásobníku.

Potvrzení nebo zrušení navigace

Aplikace může během operace navigace potřebovat interakci s uživatelem, aby uživatel mohl potvrdit nebo zrušit navigaci. To může být nutné například v případě, že se uživatel pokusí přejít před úplným dokončením stránky pro zadávání dat. V takovém případě by aplikace měla poskytnout oznámení, které uživateli umožní přejít mimo stránku nebo zrušit navigační operaci předtím, než k ní dojde. Toho lze dosáhnout ve třídě modelu zobrazení pomocí odpovědi z oznámení k řízení, zda je vyvolána nebo není vyvolána navigace.

Shrnutí

Xamarin.Forms zahrnuje podporu navigace na stránce, která obvykle vede k interakci uživatele s uživatelským rozhraním nebo ze samotné aplikace v důsledku interních změn stavu řízeného logikou. Navigace ale může být složitá pro implementaci v aplikacích, které používají model MVVM.

Tato kapitola představila NavigationService třídu, která se používá k zobrazení modelu první navigace z modelů zobrazení. Umístění navigační logiky do tříd modelu zobrazení znamená, že logiku je možné provádět prostřednictvím automatizovaných testů. Model zobrazení pak může implementovat logiku pro řízení navigace, aby se zajistilo, že se vynucují určitá obchodní pravidla.