SE(たぶん)の雑感記

一応SEやっている筆者の思ったことを書き連ねます。会計学もやってたので、両方を生かした記事を書きたいと考えています。 でもテーマが定まってない感がすごい。

UWP悪戦苦闘記 その5 HamburgerMenuでナビゲーションに苦労してみる

すごく久々の、UWPネタです。*1

2017年7月当初と、少し前のニュースになりますが、

codezine.jp

こんなのがありました。*2

MS公式のGitHubはこちら。

github.com

これを見て、使ってみよう!というのが、今回の記事です。

  • 紹介記事

最新のアプリ - UWP Community Toolkit の調査

HamburgerMenuとは

UWPアプリで見かける、「三」みたいなボタンで開閉する左のメニューです。

f:id:hiroronn:20170830205846p:plain

f:id:hiroronn:20170830205856p:plain

このToolkitが出るまでは、SplitView等を使って自作する必要がありましたが、ライブラリに含まれています。

これとPrismを組み合わせて、ナビゲーションしてみよう!という目的です。

できていないので注意!

HamburgerMenuでのページ遷移

HamburgerMenuの中には、Pageを表示できます。

上の画像で表示している、簡素な画面も、別のPageとして作成しています。

Page表示を実現するには、こんな風に

<controls:HamburgerMenu x:Name="hamburgerMenuControl"
                        ItemsSource="{x:Bind ViewModel.Menus}"
                        PaneBackground="Black"
                        Foreground="White"
                        ItemTemplate="{StaticResource DefaultTemplate}"
                        ItemClick="{x:Bind ViewModel.OnMenuItemClick}"
                        OptionsItemTemplate="{StaticResource DefaultTemplate}"
                        OptionsItemClick="{x:Bind ViewModel.OnMenuItemClick}">
    <Frame x:Name="contentFrame" Foreground="Black" Loaded="contentFrame_Loaded"/>
</controls:HamburgerMenu>

内部にFrameを置き、このフレームに対し、ナビゲーションを呼び出せばよいようです。

メニューをクリックしたら画面遷移

上で、

ItemClick="{x:Bind ViewModel.OnMenuItemClick}"

と、やっていますが、これがメニュークリック時のイベントです。

x:Bindの機能を使って、イベントを直接ViewModelとバインドして呼び出しています。

ViewModelでのイベントはこんな感じです。

private INavigationService _navigation;
private INavigationService _menuNavi

public void OnMenuItemClick(object sender, ItemClickEventArgs e) {
    var menuItem = e.ClickedItem as MenuItem;

    _menuNavi.Navigate(menuItem.PageName, null);
}

メニューの情報はViewModelで保持しており、遷移先(menuItem.PageName)も、そこで管理しています。

_menuNaviは、HamburgerMenu内のナビゲーションです。

Prismのナビゲーションの仕組み

いきなり話は変わりますが…

Prismでは、標準でナビゲーション用のインターフェイスがあり、標準実装は用意されています。*3

これを、DIコンテナを通して、各ViewModelで利用できます。

この機能、大変便利なのですが、標準だと「画面全体を遷移」させます。つまり、「遷移先として指定したページを、ウィンドウ全体に表示」します。

標準のナビゲーションでHamburgerMenuを使うと、上で書いた

内部にFrameを置き、このフレームに対し、ナビゲーションを呼び出せばよい

という部分が実現できません。

HamburgerMenuのページ独自の遷移

既に解決されている方がいらっしゃいました。

リンク

…どうやっても埋め込みがうまくいかないので、Markdown構文で埋め込みました。

ただ、DataContextChangedのタイミングだと、Prismではうまくいかないので、Frame.Loadedのタイミングで行っています。*4

*Viewソース

private void contentFrame_Loaded(object sender, RoutedEventArgs e) {
    var vm = ViewModel;
    if (vm != null) {
        Func<string, Type> typefunc = s => Type.GetType(this.GetType().Namespace + $".{s}Page");
        var frame = new FrameFacadeAdapter(contentFrame);
        var navi = new FrameNavigationService(
            frame,
            typefunc,
            new SessionStateService());

        vm.ReceiveMenuNavigation(navi);
    }
}

ViewModelには、独自のINavigationServiceを受け取る入口を作っています。(受け取ったものが、_menuNaviにセットされる)
Frameへの参照が無いと、INavigationServiceは生成できないため、DIコンテナ利用は諦めています。

こうすると、Prismの邪魔をせず、HamburgerMenuのクリックで、画面遷移ができます!



…が。

戻るボタンをどうするか

画面左上に表示される「←」ボタンですが、これはあくまでPrism標準のナビゲーションのみ解決します。
そのため、上のようにオレオレナビゲーションを行うと、HamburgerMenu内では戻る機能が使えません。*5

ここが、できていない部分です。
正直、複数ナビゲーション(複数Frame)を使った場合のナビゲーションをどう作るのが良いのか、分かっていません。

複数のナビゲーションを繋ぐ機能があるのか、何か別の方法があるのか…現在試行錯誤中です。

これが、今回の「悪戦苦闘」内容です。

おわりに

記事を書いていて、

ソース全体を出したほうがよくないか…?

と思い始めました。

GitHubアカウントに、可能なものはどんどん公開していきたいので、興味ある方は言ってください!
大変、励みになります。


*1:前回ブログの懺悔を、即座に活かした

*2:記事執筆時点で、v2.0が存在することを確認。内容は未確認

*3: Prism.Windows.PrismApplication.NavigationService のこと

*4:必要なら、コードビハインドはどんどん使っていくべきだと思う。特に、画面内部遷移は、このウィンドウの関心事なので、問題は無いはず

*5:自分でボタン置けばいいんだけれども