SplitViewコントロールとFrameコントロールを利用して、UWP内でハンバーガーメニューから画面遷移を行う方法を解説する。
powered by Insider.NET
Windows 10のユニバーサルWindowsプラットフォーム用のアプリ(以降、UWPアプリ)に特徴的なUIといえば、「ハンバーガーメニュー」(=ハンバーガーボタンによって開閉するメニュー)を使った画面遷移が挙げられる(次の画像)。これはどのようにして実装すればよいのだろうか? 複数の画面それぞれにハンバーガーメニューを実装するのだろうか? あるいは、ハンバーガーメニューの部分をユーザーコントロールとして作成し、それぞれの画面に貼り付けて使うのだろうか? 実は、Windows 10で新しく追加されたSplitViewコントロール(Windows.UI.Xaml.Controls名前空間)を使えば、ハンバーガーメニューの中で画面遷移を行えるのである。ASP.NET(Webフォーム)のマスターページとコンテンツページをご存じならば、それと同じような仕掛けだと言えばイメージは分かっていただけるだろう。
本稿では、SplitViewコントロールを使い、ハンバーガーメニューで画面遷移を実装する例を説明する。ハンバーガーボタンの実装と、ハンバーガーボタンでSplitViewコントロールを開閉する方法については、「WinRT/Metro TIPS:ハンバーガーボタンを実装するには?[Windows 10 UWPアプリ開発]」をご覧いただきたい。なお、本稿のサンプルは「Windows Store app samples:MetroTips #112」からダウンロードできる。
ハンバーガーメニューによる画面遷移の例
これはWindows 10に標準の「ニュース」アプリである。それぞれの画像の左側は、デスクトップ用のもの。右側は、Windows 10モバイルエミュレーターで動かしているモバイル用のものである。
上: ハンバーガーメニューが閉じている状態。デスクトップ用では、ハンバーガーメニューを完全に隠さず、アイコンが見えるようにしている。モバイル用では、ハンバーガーメニューを完全に隠している。ここでハンバーガーボタン(左上の赤色のボタン)をタップ/クリックすると、ハンバーガーメニューが開く。
中: ハンバーガーメニューが開いている状態。このアプリでは、ハンバーガーメニューに表示されている項目の中で、現在表示している画面(この画像では[マイニュース]/[My News])に該当する項目の背景色を変えている(暗赤色)。ここで、例えば[カテゴリ]/[Interests]項目をタップ/クリックすると、ハンバーガーメニューは閉じられ、その画面に遷移する。
下: 画面遷移した状態。デスクトップ用では、縮小されたハンバーガーメニューの中で、遷移した画面に該当するアイコンの背景色が変わっている(モバイル用も同様なのだが、ハンバーガーメニューが完全に隠されているため、この画像では分からない)。
デスクトップ用とモバイル(=Windows 10を搭載したスマートフォン)用のUWPアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Community 2015を使っている。
本稿では、「WinRT/Metro TIPS:ハンバーガーボタンを実装するには?[Windows 10 UWPアプリ開発]」を前提とする。ハンバーガーボタンのタップ/クリックでハンバーガーメニューを開閉する実装については、そちらをご覧いただきたい。
*1 SLAT対応ハードウエアは、モバイルエミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能である。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウエアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。
*2 Windows 10モバイルエミュレーターを使用しないのであれば、32bit版でもよい。
*3 開発に使うWindows 10は「開発者モード」を有効にしておくこと(「設定アプリ」の[更新とセキュリティ]−[開発者向け]で、[開発者モード]ラジオボタンを選択)。そうしないと、VS 2015のXAMLエディターがエラーになる。また、本稿の内容が適用できるのは、Windowsのライフサイクル管理の上では「Windows 10, released in July 2015」と呼ばれるリリース、またはそれ以降。WinVerコマンドで表示されるバージョンは「10.0 (ビルド 10240)」。UWPアプリ開発におけるデバイスファミリーのバージョン指定としては「10.0.0.0」(Package.appxmanifestファイルのTargetDeviceFamily)である。なお、モバイルエミュレーターを使用しないのであれば、Home版でもよい。
*4 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2015 for Windows 10(製品版)はマイクロソフトのページから無償で入手できる(ページの左側で[Visual Studio 2015]−[Express 2015 for Windows 10]と選ぶ)。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、UWPアプリの開発には「for Windows 10」を使う(「for Desktop」はWPF/Windowsフォーム/Win32 APIのアプリ開発用)。また、Visual Studio Community 2015(製品版)もマイクロソフトのページから無償で入手できる。なお、英語版がインストールされた場合には、Microsoft Visual Studio 2015 Language Packの日本語版を追加インストールし、[オプション]ダイアログで言語を切り替える。
*5 使用しているVS 2015に含まれていない場合は、Windows SDK for Windows 10のページからダウンロードできる。本稿の内容が適用できるのは、作成したプロジェクトのプロパティに表示されるターゲットバージョンが「10.0; ビルド 10240」またはそれ以降。
最初に、ハンバーガーメニュー(SplitViewコントロール)と遷移する画面との関係を説明しておこう。
本稿で実装するUIは、次の画像のようなものだ。
このUIの構造は、その概略を示すと次の図のようになっている。
重要な点は、SplitViewコントロールのContentプロパティにFrameコントロール(Windows.UI.Xaml.Controls名前空間)を配置でき、そのFrameコントロールの中で通常のページをナビゲートできることである。
先に、上の図の「Page1.xaml」/「Page2.xaml」/「Page3.xaml」を作成しておこう(次のコード)。テキストブロックの他に、今回は[戻る]ボタンも実装しておく(ただし、これには問題がある。後述する)。
<Page
……省略……
mc:Ignorable="d">
<Grid RequestedTheme="Light" Background="#fff0f0">
<!-- [戻る]ボタン -->
<Button x:Name="BackButton" Click="BackButton_Click"
Width="48" VerticalAlignment="Top" Background="Transparent">
<Button.Content>
<SymbolIcon Symbol="Back" />
</Button.Content>
</Button>
<!-- テキストブロック -->
<TextBlock FontFamily="Yu Gothic" FontSize="40"
HorizontalAlignment="Center" VerticalAlignment="Center">
ページ【1】
</TextBlock>
</Grid>
</Page>
public sealed partial class Page1 : Page
{
public Page1()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
BackButton.Visibility
= Frame.CanGoBack ? Visibility.Visible : Visibility.Collapsed;
}
private void BackButton_Click(object sender, RoutedEventArgs e)
{
Frame.GoBack();
}
}
Public NotInheritable Class Page1
Inherits Page
Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
MyBase.OnNavigatedTo(e)
BackButton.Visibility _
= If(Frame.CanGoBack, Visibility.Visible, Visibility.Collapsed)
End Sub
Private Sub BackButton_Click(sender As Object, e As RoutedEventArgs)
Frame.GoBack()
End Sub
End Class
ハンバーガーメニューに表示する項目の実装は、さまざまな方法が考えられる。画面遷移を伴わない機能、例えばフライアウトを出すといった用途では、通常のボタンでよいだろう。しかし、画面遷移をさせる場合は項目の選択状態を維持する必要がある。そうしないと、現在どの画面を表示しているのか(=どの項目を選択しているのか)が分からなくなってしまうからだ。すると、画面遷移を伴う機能を実装するときには、トグルボタンが使えそうである。ただし、もう一つ課題がある。同じ項目を2回タップ/クリックしても選択状態が解除されてはいけないのだ(同じ画面が表示されているのであるから)。トグルボタンを使う場合には、その対処が必要になる。
ここでは、ラジオボタンを利用してみよう。選択状態は維持してくれるし、選択された状態の項目をさらにタップ/クリックしてもイベントは発生しない。画面遷移用の項目にはぴったりだ。外観には問題があるが、それは後からスタイルを変更しよう。
すると、「MainPage.xaml」のSplitViewコントロールの部分は、次のコードのようになる。
<SplitView x:Name="Splitter" DisplayMode="CompactOverlay"
CompactPaneLength="48" OpenPaneLength="320"
……省略……
PaneBackground="DarkSeaGreen">
<!-- Paneプロパティにはメニューを入れる -->
<SplitView.Pane>
<Grid RequestedTheme="Dark">
<Grid.RowDefinitions>
……省略……
</Grid.RowDefinitions>
<!-- 上部のナビゲーションボタン -->
<StackPanel Margin="0,32,0,0">
<RadioButton x:Name="RadioButton1"
Checked="RadioButton1_Checked"
GroupName="Navi"
>ページ【1】</RadioButton>
<RadioButton x:Name="RadioButton2"
Checked="RadioButton2_Checked"
GroupName="Navi"
>ページ【2】</RadioButton>
<RadioButton x:Name="RadioButton3"
Checked="RadioButton3_Checked"
GroupName="Navi"
>ページ【3】</RadioButton>
</StackPanel>
<!-- 下端の[About]ボタン -->
……省略……
</Grid>
</SplitView.Pane>
<!-- ContentプロパティにはFrameコントロールを入れる -->
<SplitView.Content>
<Frame x:Name="MainContentFrame" />
</SplitView.Content>
</SplitView>
そして、画面遷移の項目がタップ/クリックされたときのイベントハンドラーは、次のコードのようになる。実にシンプルである。
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
……省略……
// アプリ開始時には、ページ【1】を表示する
RadioButton1.IsChecked = true;
}
// ハンバーガーメニューで[ページ【1】]が新たに選択された
private void RadioButton1_Checked(object sender, RoutedEventArgs e)
{
MainContentFrame.Navigate(typeof(Page1));
Splitter.IsPaneOpen = false;
}
// ハンバーガーメニューで[ページ【2】]が新たに選択された
private void RadioButton2_Checked(object sender, RoutedEventArgs e)
{
MainContentFrame.Navigate(typeof(Page2));
Splitter.IsPaneOpen = false;
}
// ハンバーガーメニューで[ページ【3】]が新たに選択された
private void RadioButton3_Checked(object sender, RoutedEventArgs e)
{
MainContentFrame.Navigate(typeof(Page3));
Splitter.IsPaneOpen = false;
}
}
Public NotInheritable Class MainPage
Inherits Page
Protected Overrides Async Sub OnNavigatedTo(e As NavigationEventArgs)
……省略……
' アプリ開始時には、ページ【1】を表示する
RadioButton1.IsChecked = True
End Sub
' ハンバーガーメニューで[ページ【1】]が新たに選択された
Private Sub RadioButton1_Checked(sender As Object, e As RoutedEventArgs)
MainContentFrame.Navigate(GetType(Page1))
Splitter.IsPaneOpen = False
End Sub
' ハンバーガーメニューで[ページ【2】]が新たに選択された
Private Sub RadioButton2_Checked(sender As Object, e As RoutedEventArgs)
MainContentFrame.Navigate(GetType(Page2))
Splitter.IsPaneOpen = False
End Sub
' ハンバーガーメニューで[ページ【3】]が新たに選択された
Private Sub RadioButton3_Checked(sender As Object, e As RoutedEventArgs)
MainContentFrame.Navigate(GetType(Page3))
Splitter.IsPaneOpen = False
End Sub
End Class
Copyright© Digital Advantage Corp. All Rights Reserved.