Prism使ってやってみましょう。UWPアプリを作ってPrism.Unityを追加してAppクラスを書き換えます。
XAML側
<Prism:PrismUnityApplication x:Class="App14.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App14"xmlns:Prism="using:Prism.Unity.Windows"RequestedTheme="Light"></Prism:PrismUnityApplication>
C#側
using App14.ViewModels; using App14.Views; using Microsoft.Practices.Unity; using Prism.Unity.Windows; using System.Diagnostics; using System.Threading.Tasks; using Windows.ApplicationModel.Activation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace App14 { sealedpartialclass App : PrismUnityApplication { public App() { Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync( Microsoft.ApplicationInsights.WindowsCollectors.Metadata | Microsoft.ApplicationInsights.WindowsCollectors.Session); this.InitializeComponent(); } protectedoverride Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args) { this.NavigationService.Navigate("Main", null); return Task.CompletedTask; } } }
画面遷移テスト用に3つくらい画面を作ります。ViewsフォルダにMainPage.xaml、NextPage.xaml、AboutPage.xamlくらい作ります。
Shellっていう名前でSplitViewを持ったページを作ります。こいつは、SplitViewを持ったページです。
<Page x:Class="App14.Views.Shell"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App14.Views"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:Mvvm="using:Prism.Windows.Mvvm"Mvvm:ViewModelLocator.AutoWireViewModel="True"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><SplitView x:Name="FrameHost"x:FieldModifier="public"IsPaneOpen="True"DisplayMode="CompactInline"><SplitView.Pane><ListView ></ListView></SplitView.Pane></SplitView></Grid></Page>
ListViewにページ名を出して、右側にページを表示するようにします。PrismでSplitViewとかを持ったクラスを作るには、AppクラスのCreateShellメソッドをオーバーライドして渡されたFrameを使ってページを構築します。今回の場合SplitViewにFrameHostという名前を付けてるので、こいつのContentにFrameを突っ込みます。
protectedoverride UIElement CreateShell(Frame rootFrame) { var shell = this.Container.Resolve<Shell>(); shell.FrameHost.Content = rootFrame; return shell; }
なんとなく下地ができたので、ページ遷移を管理するクラスを作ります。Prismで画面遷移するためのページ名を管理するクラスです。ページクラスからページ名への変換ロジックも持たせてあります。後で使います。
using Prism.Mvvm; using System; using System.Collections.ObjectModel; namespace App14.ViewModels { publicclass NavigationStateManager : BindableBase { public ObservableCollection<string> PageTokens { get; } = new ObservableCollection<string>(); privatestring currentPageToken; publicstring CurrentPageToken { get { returnthis.currentPageToken; } set { this.SetProperty(refthis.currentPageToken, value); } } publicvoid SetCurrentPageTokenFromPageType(Type pageType) { var typeName = pageType.Name; this.CurrentPageToken = typeName.Substring(0, typeName.Length - 4); } } }
こいつをUnityのコンテナにシングルトンで登録します。そして、FrameのNavigatedイベントで現在のページをセットするようにします。CurrentPageTokenが変わったら、そのページに画面遷移もするようにしておきましょう。ということでAppクラスが以下のように化けます。
using App14.ViewModels; using App14.Views; using Microsoft.Practices.Unity; using Prism.Unity.Windows; using System.Diagnostics; using System.Threading.Tasks; using Windows.ApplicationModel.Activation; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace App14 { sealedpartialclass App : PrismUnityApplication { public App() { Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync( Microsoft.ApplicationInsights.WindowsCollectors.Metadata | Microsoft.ApplicationInsights.WindowsCollectors.Session); this.InitializeComponent(); } protectedoverridevoid ConfigureContainer() { base.ConfigureContainer(); // NavigationStateManagerをシングルトンで管理してもらうthis.Container.RegisterType<NavigationStateManager>(new ContainerControlledLifetimeManager()); } protectedoverride Frame OnCreateRootFrame() { // Frameをカスタマイズ var frame = new Frame(); frame.Navigated += (_, e) => { this.Container.Resolve<NavigationStateManager>().SetCurrentPageTokenFromPageType(e.SourcePageType); }; return frame; } protectedoverride Task OnInitializeAsync(IActivatedEventArgs args) { // 初期化 var navigationStateManager = this.Container.Resolve<NavigationStateManager>(); navigationStateManager.PageTokens.Add("Main"); navigationStateManager.PageTokens.Add("Next"); navigationStateManager.PageTokens.Add("About"); navigationStateManager.CurrentPageToken = "Main"; // CurrentPageTokenが変わったら画面遷移するthis.Container.Resolve<NavigationStateManager>() .PropertyChanged += (sender, e) => { if (e.PropertyName == nameof(NavigationStateManager.CurrentPageToken)) { this.NavigationService.Navigate(navigationStateManager.CurrentPageToken, null); } }; return Task.CompletedTask; } protectedoverride UIElement CreateShell(Frame rootFrame) { var shell = this.Container.Resolve<Shell>(); shell.FrameHost.Content = rootFrame; return shell; } protectedoverride Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args) { this.NavigationService.Navigate("Main", null); return Task.CompletedTask; } } }
これで、NavigationStateManagerの状態に同期して画面遷移するようになりました。あとは、NavigationStateManagerをSplitViewの左側のListViewにバインドするだけです。ShellViewModelクラスを作って、以下のようにNavigationStateManagerを受け取って外部に公開しましょう。
using Prism.Windows.Mvvm; namespace App14.ViewModels { publicclass ShellViewModel : ViewModelBase { public NavigationStateManager NavigationStateManager { get; } public ShellViewModel(NavigationStateManager navigationStateManager) { this.NavigationStateManager = navigationStateManager; } } }
そして、Shell.xamlでListViewにバインドします。バインドするのは、PageTokensプロパティとCurrentPageTokenプロパティになります。
<Page x:Class="App14.Views.Shell"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App14.Views"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:Mvvm="using:Prism.Windows.Mvvm"Mvvm:ViewModelLocator.AutoWireViewModel="True"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><SplitView x:Name="FrameHost"x:FieldModifier="public"IsPaneOpen="True"DisplayMode="CompactInline"><SplitView.Pane><ListView ItemsSource="{x:Bind ViewModel.NavigationStateManager.PageTokens}"SelectedItem="{x:Bind ViewModel.NavigationStateManager.CurrentPageToken, Mode=TwoWay}"></ListView></SplitView.Pane></SplitView></Grid></Page>
実行すると以下のようになります。初期状態ではMainPageが表示されてます。左側のListViewの選択もMainになってるのが確認できます。
ListViewを操作すると、画面遷移していくことが確認できます。下図は、Aboutをクリックしたときの様子です。
戻るボタンを押すときちんとページが戻ってListViewの選択も同期してることが確認できます。
まとめ
ということで、SplitViewの左側のページのリストと実際に右側に表示されてるページの同期をとってみました。管理クラスを作って、ページの状態をそれと同期させるというアプローチです。もうちょっと複雑な要件になってくると、もうちょっと賢く作らないといけないかもしれないですね。(ページのパラメータ渡すとかetc...)