nuits.jp blog

C#, Xamarin, WPFを中心に書いています。Microsoft MVP for Development Technologies。

Prism for Xamarin.Forms入門 NavigationService Deep Dive

さて、画面遷移と前画面への戻り方を説明いたしました。

www.nuits.jp

今回は、もう少し踏み込んで画面遷移の要素を一つずつ見ていきたいと思います。

なお本エントリーは連載記事「Prism for Xamarin.Forms入門」の一部となっております。
以下に目次がありますので、他のエントリーもご覧いただけると嬉しいです。 【Xamarin】Prism.Forms入門 目次 - nuits.jp blog

それでは、さっそく本題へ入っていきましょう。  

画面遷移の相対パスと絶対パス

Prism.Formsでは、INavigationServiceのNavigationAsyncメソッドを利用して画面遷移することは、前回説明いたしました。
この時、前回は次のようなコードで画面遷移を指定しました。

_navigationService.NavigateAsync("SecondPage");

これは、画面遷移の相対パスによる指定になります。
以下のようにスラッシュ始まりで指定することで、絶対パスで指定することができます。

_navigationService.NavigateAsync("/SecondPage");

絶対パスで指定した場合、ナビゲーションスタックがリセットされ、トップ画面から再遷移が行われます。
以下にそれぞれMainPage>SecondPageで画面遷移した場合の、相対パス指定と絶対パス指定の違いを図示してみました。
相対パス指定した場合、SecondPageからMainPageへ戻ることができますが、絶対パス指定の場合、1ページ目が置き換わるため戻ることができません。

f:id:nuitsjp:20160821232357p:plain

逆にいうと、ナビゲーションスタックをリプレースすることができる、という事も出来ます。

画面遷移イベントのハンドル

Prism.Formsでは、画面遷移の出入りそれぞれのタイミングを、ViewModel側でハンドルする仕組みが用意されています。
ViewModelで、INavigationAwereを実装することでViewModel側でイベントをハンドルできます。
INavigationAwereには二つのメソッドが定義されています。

名称 説明
OnNavigatedTo 画面へ遷移してきたタイミングで呼び出される
OnNavigatedFrom 別画面へ遷移するタイミングで呼び出される

OnNavigatedToの最も頻度の高い利用目的は、画面遷移時のパラメータの取得でしょう。
OnNavigatedFromでは、画面遷移の際にリソースの開放などに利用できます。

INavigationAwereについては、Prism Template Packで作成したMainPageのViewModelが実装しているので、そちらを見てみるのが早いでしょう。
具体的には以下のような実装がなされています。

public class MainPageViewModel : BindableBase, INavigationAware
{
    public void OnNavigatedTo(NavigationParameters parameters)
    {
        if (parameters.ContainsKey("title"))
            Title = (string)parameters["title"] + " and Prism";
    }

    public void OnNavigatedFrom(NavigationParameters parameters)
    {

    }
}

OnNavigatedToは、次の画面からGoBackで戻ってきたときにも呼び出されますが、その際は最初に遷移してきた際に渡されたパラメータは渡されない為、注意が必要です。

なおINavigationAwereでは、画面遷移を抑止するような用途には利用できません。
その場合は次の項を参照してください。

画面遷移の抑止

例えば、入力フォームで必須項目が未入力の場合に、その画面を遷移させたくないといったケースがあるかと思います。
Prism.Formsでは、ViewModelでIConfirmNavigationを実装することで、画面遷移を抑止することができます。
IConfirmNavigationのCanNavigateメソッドでfalseを返すことで画面遷移を抑止できます。

具体的な実装例を以下に紹介します。

まず、MainPage画面にトグルスイッチを配置し、トグルがONの場合は遷移を許可し、OFFの場合は遷移を抑止することとします。
以下がMainPageのXAMLです。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="NsDeepDive.Views.MainPage"
             Title="MainPage">
  <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <Switch IsToggled="{Binding CanNavigateSecond}" HorizontalOptions="Center"/>
    <Button Text="Navigate Second" Command="{Binding NavigateSocondCommand}"/>
  </StackLayout>
</ContentPage>

そして以下がViewModelの実装です。

public class MainPageViewModel : BindableBase, IConfirmNavigation
{
    private readonly INavigationService _navigationService;
    public bool CanNavigateSecond { get; set; }
    public ICommand NavigateSocondCommand { get; }

    public MainPageViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;
        NavigateSocondCommand = new DelegateCommand(() =>
        {
            _navigationService.NavigateAsync("SecondPage");
        });
    }

    public void OnNavigatedTo(NavigationParameters parameters)
    {
    }
    public bool CanNavigate(NavigationParameters parameters)
    {
        return CanNavigateSecond;
    }
    public void OnNavigatedFrom(NavigationParameters parameters)
    {
    }
}

画面のSwitchのIsToggledをVMのCanNavigateSecondにバインドしています。
そしてCanNavigateメソッドの戻り値としてCanNavigateSecondを返却しています。

この結果、以下のようにSwitchがONの場合のみ、画面遷移が実行されます。

f:id:nuitsjp:20160821232452g:plain

ぶっちゃけ、そもそもボタン押せないように制御すればいいのでは?と、思わないでもないですが何か他にいい活用方法があるのかもしれません。

画面遷移時のパラメータの受け渡し

画面遷移時に次画面へパラメーターを渡したい場合、以下のようにしてパラメーターを渡すことができます。
NavigationParametersは内部的にDictionaryであるため、パラメータの値にはオブジェクトを渡すことが可能です。

var navigationParameters = new NavigationParameters();
navigationParameters["customer"] = new Customer();
_navigationService.NavigateAsync("SecondPage", navigationParameters);

また、別解として遷移先の指定箇所で、URLのクエリー文字列のような形でパラメータ渡しをすることが可能です。
これはPrism Template Packで作成されるApp.xaml.csからMainPageへの画面遷移で利用されているため、ご存知の方も多いでしょう。
ちなみに、以下の3つはすべて同じ意味を表し、場合によって使い分けが可能です。

NavigationService.NavigateAsync("MainPage?title=Hello%20from%20Xamarin.Forms");
var navigationParameters = new NavigationParameters();
navigationParameters["title"] = "Hello from Xamarin.Forms";
NavigationService.NavigateAsync("MainPage", navigationParameters);
var navigationParameters = new NavigationParameters("title=Hello%20from%20Xamarin.Forms");
NavigationService.NavigateAsync("MainPage", navigationParameters);

また以下のように複合的に利用することも可能です。(するかどうかはさておき)

var navigationParameters = new NavigationParameters("key1=value1");
navigationParameters["key1"] = "value2";
NavigationService.NavigateAsync("MainPage?key3=value3", navigationParameters);

Deep Linking

Prism.Formsでは非常にユニークな画面遷移が提供されています。
それがDeep Linkingです。
これは、例えばアプリケーション外からURLなどでアプリケーションの特定のページを直接起動する場合などに利用できます。

具体的な使用イメージは以下のような感じです。  

f:id:nuitsjp:20160821232530p:plain

パスを結合して指定することで、深いページへの直接的な遷移と、その間のナビゲーションスタックを再現します。
また過程でパラメータを指定することも可能です。  

f:id:nuitsjp:20160821232542p:plain

とはいえ、やはりDeep Linkingはやや扱いの難しいでしょうし、これだけでは具体的なイメージが湧きにくいと思いますので、次回以降、個別に詳細を掘り下げたいと思います。
少々お待ちください。

と言う分けで、NavigationServiceの要素を一通り説明したところで、今回はここまでとしたいと思います。
それではまた!