INavigationService.NavigateAsync で遷移先ページの ViewModel が生成されるタイミングについて
昨日開催されました @nuits_jpさん による「JXUGC #18 フォローアップハンズオン Prism & Moqをさわってみよう!」に参加してきました.
ハンズオンでしょーもないことにハマってしまったのですが、それについて懇親会でにゅいさんに教えていただきました.
今回は Prism.Forms の INavigationService.NavigateAsync で、遷移先ページの ViewModel が生成されるタイミングについてです.
ViewModel が生成されるタイミング
namespace PrismSample.ViewModels
{
public class MainPageViewModel : BindableBase, IConfirmNavigationAsync
{
private readonly IPageDialogService _pageDialogService;
private readonly INavigationService _navigationService;
public ICommand NavigationCommand => new DelegateCommand(Navigate);
public MainPageViewModel(INavigationService navigationService, IPageDialogService pageDialogService)
{
_navigationService = navigationService;
_pageDialogService = pageDialogService;
}
public void Navigate()
{
_navigationService.NavigateAsync("SecondPage");
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
}
public async Task CanNavigateAsync(NavigationParameters parameters)
{
return await _pageDialogService.DisplayAlertAsync("Navigation", "Navigate to Second ?", "Yes", "No");
}
}
}
遷移元ページがこのように、ページ遷移する際に確認ダイアログを表示する機能を持ちます.
普通だったら、CanNavigateAsync が呼び出されたあとに遷移先ページと遷移先ページの ViewModel が生成されそうな気がしますが、実は NavigateAsync の段階ですでに ViewModel のコンストラクタが呼び出されます.
そして今回、私がハマってしまったのは、遷移先ページの ViewModel でクラッシュするコードを書いてしまったことです.
namespace PrismSample.ViewModels
{
public class SecondPageViewModel : BindableBase
{
public SecondPageViewModel()
{
Debug.WriteLine("SecondPageViewModel called ...");
string s = null;
var sub = s.Substring(0, 10);
}
}
}
例えば、このように ViewModel のコンストラクタでクラッシュするコードを書きます.
このとき起こる挙動としては、ボタンを押してもダイアログが表示されず画面遷移も行われません.
NavigateAsync に await を付けない限り例外は握りつぶされてしまいます.
あたかも遷移元に不備があるような挙動になるわけです.
これまでのことをまとめます.
- 遷移先ページの ViewModel が生成されるのは CanNavigateAsync の前で、NavigateAsync の段階ですでに生成されている
- 遷移先ページの ViewModel の例外は await を付けない限りキャッチされない
ダイアログの可否に関わらず ViewModel が生成されるわけなので、ViewModel のコンストラクタでは ReactiveProperty の初期化などにとどめるのが良いでしょう(にゅいさん談).
また、NavigateAsync まわりで意図しない挙動が発生した場合、一旦 await を付けるとエラーの内容が鮮明になるかもしれません.
多階層の画面遷移ではどうなるか
public void Navigate()
{
_navigationService.NavigateAsync("SecondPage/ThirdPage/ForthPage");
}
先程の例で多階層の画面遷移を行う場合の ViewModel 生成のタイミングはどうなるでしょうか.
結果は、NavigateAsync のタイミングで SecondPageViewModel, ThirdPageViewModel, ForthPageViewModel の順番でコンストラクタが呼び出されます.
多階層にしても結果は同じでした。
以上です.
ディスカッション
コメント一覧
まだ、コメントがありません