Prism.WpfでRequestNavigateと戻る、進む(IRegionNavigationService, IRegionNavigationJournal)

# はじめに
いつもお世話になっている[Prism][]なんですけど、使い方を習熟するべく元のソースを見ていたら知らない機能があったため、勉強がてら投稿させていただきます。

# 機能確認用プロジェクト

[Prism7Sample(自作)](https://github.com/kwhrkzk/Prism7Sample/tree/master/RegionNavigationJournalSample)
[Prism-Samples-Wpf(公式) 24-NavigationJournal](https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/24-NavigationJournal)

# 環境
Prism.DryIoc.7.1.0.172-pre
Prism.Wpf.7.1.0.172-pre
Prism.Core.7.1.0.172-pre
.NET Framework: 4.7.2

# [IRegionNavigationService][]について
皆様は[IRegionNavigationService][]の存在はご存知でしたでしょうか。[INavigationAware][]の[OnNavigatedTo][INavigationAware]や[OnNavigatedFrom][INavigationAware]の仮引数である[NavigationContext][]のメンバである[NavigationService][NavigationContext]から取得できます。

```csharp:xxxViewModel.cs
        private IRegionNavigationService RegionNavigationService { get; set; }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            RegionNavigationService = navigationContext.NavigationService;
        }
```

この[IRegionNavigationService][]は[INavigateAsync][]を継承しているため、[IRegionManager][]をDIコンテナから注入しなくても[RequestNavigate][INavigateAsync]することができます。しかも[IRegionManager][]で[RequestNavigate][IRegionManager]するときは毎回RegionNameを指定する必要がありましたが[IRegionNavigationService][]の[RequestNavigate][INavigateAsync]では記述する必要がありません。

```csharp:IRegionManager例
RegionManager.RequestNavigate("ContentRegion", nameof(yyyView));
```

```csharp:IRegionNavigationService例
RegionNavigationService.RequestNavigate(nameof(yyyView));
```

同一Region内で画面遷移をするのならば[IRegionNavigationService][]、別のRegionの画面を操作する必要がある場合は[IRegionManager][]と使い分けできます。

# [IRegionNavigationJournal][]について
また、[IRegionNavigationService][]は[Journal][IRegionNavigationService]というメンバも持っています。この[Journal][IRegionNavigationService]は[IRegionNavigationJournal][]の型で名前の通り、Region内のナビゲーションの履歴を管理してくれています。[IRegionNavigationService][]の[RequestNavigate][INavigateAsync]を使用して画面遷移していると[GoBack][IRegionNavigationJournal]や[GoForward][IRegionNavigationJournal]を使用して戻る進む機能を実現できます。例えば段階的に画面が遷移するようなシステムでひとつ前に戻るようなボタンを実装する際は[RequestNavigate][INavigateAsync]で個別のView名を記述するよりも[GoBack][IRegionNavigationJournal]で実装するほうが問題になりにくいのではないかと思います。
以下に戻る、進むボタンの実装例を記載します。

```csharp:ContentViewModel.cs
    public class ContentViewModel : BindableBase, INavigationAware
    {
        private IRegionNavigationService RegionNavigationService { get; set; }

        public DelegateCommand BackCommand { get; }
        public DelegateCommand ForwardCommand { get; }

        public ContentViewModel()
        {
            BackCommand = new DelegateCommand(() => RegionNavigationService.Journal.GoBack(), () => RegionNavigationService?.Journal?.CanGoBack ?? false);
            ForwardCommand = new DelegateCommand(() => RegionNavigationService.Journal.GoForward(), () => RegionNavigationService?.Journal?.CanGoForward ?? false);
        }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            RegionNavigationService = navigationContext.NavigationService;
            BackCommand.RaiseCanExecuteChanged();
            ForwardCommand.RaiseCanExecuteChanged();
        }

        public bool IsNavigationTarget(NavigationContext navigationContext) => true;

        public void OnNavigatedFrom(NavigationContext navigationContext){}
    }

```

# [IJournalAware][]について
ViewModelに[IJournalAware][]を継承していると[IRegionNavigationJournal][]での履歴の保存可否を指定することができます。
例えばメニュー画面->認証画面->詳細画面と遷移するようなシステムにおいて、詳細画面からメニュー画面に戻りたい(認証画面に[GoBack][IRegionNavigationJournal]したくない)等で必要になるかと思います。ViewModelにて[IJournalAware][]を継承していない場合は保存する(true)を返すようになっていました。

```csharp:IJournalAware例
    public class ContentViewModel : BindableBase, IJournalAware
    {
        public bool PersistInHistory() => true;
    }

```

# [RecordNavigation][RegionNavigationJournal]について
履歴の記録は[IRegionNavigationJournal][]の[RecordNavigation][IRegionNavigationJournal]を実行することで自分で保存することもできます。しかし[IRegionNavigationService][]の実装である[RegionNavigationService][]の[RequestNavigate][RegionNavigationService]にて[RecordNavigation][RegionNavigationJournal]してくれているので自分で実行する機会はあんまりないかと思います。
また[IRegionManager][]の実装である[RegionManager][]の[RequestNavigate][RegionManager]では[RecordNavigation][IRegionNavigationJournal]していないので注意が必要です。
戻る進む機能を利用する際は、[RegionNavigationService][]の[RequestNavigate][RegionNavigationService]を使用するようにしましょう。

# まとめ

* 同一Region内で遷移するなら[IRegionManager][]よりも[IRegionNavigationService][]
* [戻る進む機能(IRegionNavigationJournal)][IRegionNavigationJournal]便利




[RegionManager]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/RegionManager.cs
[RegionNavigationService]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/RegionNavigationService.cs
[RegionNavigationJournal]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/RegionNavigationJournal.cs
[IJournalAware]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/IJournalAware.cs
[IRegionNavigationJournal]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/IRegionNavigationJournal.cs
[INavigateAsync]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/INavigateAsync.cs
[NavigationContext]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/NavigationContext.cs
[INavigationAware]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/INavigationAware.cs
[IRegionNavigationService]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/IRegionNavigationService.cs
[IRegionManager]:https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Wpf/Regions/IRegionManager.cs
[Prism]:https://github.com/PrismLibrary/Prism