Quantcast
Channel: かずきのBlog@hatena
Viewing all articles
Browse latest Browse all 1387

UWPでSplitViewの左側のページ名のリストと右側の実際の表示されてるページを同期させる

$
0
0

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くらい作ります。

f:id:okazuki:20160412213539p:plain

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になってるのが確認できます。

f:id:okazuki:20160412214532p:plain

ListViewを操作すると、画面遷移していくことが確認できます。下図は、Aboutをクリックしたときの様子です。

f:id:okazuki:20160412214621p:plain

戻るボタンを押すときちんとページが戻ってListViewの選択も同期してることが確認できます。

f:id:okazuki:20160412214704p:plain

まとめ

ということで、SplitViewの左側のページのリストと実際に右側に表示されてるページの同期をとってみました。管理クラスを作って、ページの状態をそれと同期させるというアプローチです。もうちょっと複雑な要件になってくると、もうちょっと賢く作らないといけないかもしれないですね。(ページのパラメータ渡すとかetc...)


Viewing all articles
Browse latest Browse all 1387

Trending Articles