Win 8アプリでは実装が面倒だったポップアップ。Win 8.1では新たに追加されたフライアウト表示用のコントロールを使うことでより手軽に実装できる。
powered by Insider.NET
Windows 8用のWindowsストア・アプリ(以降、Win 8アプリ)でフライアウト(=ポップアップ)を出すのは、結構面倒だった。しかしWindows 8.1(以降、Win 8.1)用のWindowsストア・アプリ(以降、Win 8.1アプリ)では、新たに追加されたフライアウト表示用のコントロールを使えば、格段に簡単に実装できる。本稿では、それらの新しいコントロールを使う方法を解説する*1。本稿のサンプルは「Windows Store app samples:MetroTips #54(Windows 8.1版)」からダウンロードできる。
*1本稿はPreview版に基づいて記述しているため、製品版では異なる可能性がある。あらかじめご承知おきいただきたい。なお、本稿の内容は、「特集:次期Windows 8.1&Visual Studio 2013 Preview概説(後編):大きく変わるWindowsストア・アプリ開発 〜 そのほかの変更点 (3/4)」でも簡単に紹介している。併せてお読みいただければ幸いである。
Win 8.1アプリを開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要である。本稿執筆時点ではいずれもまだ製品版は一般公開されておらず、本稿ではOracle VM VirtualBox上で64bit版Windows 8.1 Pro PreviewとVisual Studio Express 2013 Preview for Windowsを使用している。これらを準備する方法や注意事項は、「WinRT/Metro TIPS:Win8用のソース・コードをWin8.1用に変換するには?[Win 8.1]」の記事をご参照いただきたい。
XAMLで画面を作るWin 8アプリでは「ポップアップ」と呼び、HTMLで画面を作るWin 8アプリでは「フライアウト」と呼んでいた。Win 8.1アプリでは「フライアウト」に呼び方が統一されるようだ。
フライアウトは一時的に表示されるUI(ユーザー・インターフェイス)で、画面のほかの場所をタップ/クリックすれば閉じるものだ。フライアウトの使用例としては、[設定]チャームから呼び出すオプション設定パネルや、画面のオブジェクト上に出すショートカット・メニュー(=コンテキスト・メニュー、ポップアップ・メニュー)などがある(次の画像)。
*2Win 8でのコード例は「MSDNブログ」の「XAMLでFlyout - Windows 8 ストアアプリテンプレート」などを参照してもらいたい。また、上の画像のアプリのコード(旧バージョン)を「クラウディアさんタイマー (Claudia Madobe Timer): XAML/C# [多言語化対応]」で公開している。
便利なフライアウトのコントロールがWindows.UI.Xaml.Controls名前空間に3つ追加された(次の表)。
コントロール | 説明 |
---|---|
Flyout | 汎用的なフライアウトを作る |
MenuFlyout | ショートカット・メニューを作る。 PopupMenuクラスとは異なり、メニューはXAMLコード側に定義できる |
SettingsFlyout | オプション設定パネルを作る |
Win 8.1アプリに追加された3つのフライアウト・コントロール |
本稿では、これらの使い方を説明していく。
Buttonコントロール(Windows.UI.Xaml.Controls名前空間)などのいくつかのコントロールには、Win 8.1でFlyoutプロパティが新設された。このプロパティにFlyoutコントロールかMenuFlyoutコントロールを設定するだけでフライアウトが実装できるのだ(前回掲載したコードを再掲)。
<AppBarButton Label="……省略……" >
<AppBarButton.Icon>
……省略……
</AppBarButton.Icon>
<!-- メニュー専用のフライアウト -->
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="メニュー1" />
<MenuFlyoutItem Text="メニュー2" />
<MenuFlyoutSeparator />
<ToggleMenuFlyoutItem Text="メニュー3(トグル)" />
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
この方法は前回「WinRT/Metro TIPS:アプリ・バーを簡単に実装するには?[Windows 8.1ストア・アプリ開発]」で紹介したので、そちらをご覧いただきたい。
Flyoutプロパティがないコントロール、例えばGridコントロール(Windows.UI.Xaml.Controls名前空間)などでFlyoutコントロールやMenuFlyoutコントロールを出すには、どうしたらよいだろうか?
それら2つのコントロールの共通の親クラスはFlyoutBaseクラス(Windows.UI.Xaml.Controls.Primitives名前空間)である。そのFlyoutBaseクラスにあるAttachedFlyout添付プロパティを使うことで任意のUIコントロールにFlyoutコントロール/MenuFlyoutコントロールを付けられるのだ。
例として、Gridコントロールをダブル・タップ/ダブル・クリックしたときにFlyoutコントロールを表示してみよう。次のコードのように、Gridコントロールの定義の中にFlyoutBase.AttachedFlyout添付プロパティを追加する。また、Gridコントロールの開始タグには、DoubleTappedイベント・ハンドラも追加する。
<Grid ……省略……
DoubleTapped="RootGrid_DoubleTapped">
<FlyoutBase.AttachedFlyout>
<Flyout x:Name="Flyout1" Placement="Bottom" >
<!-- フライアウトのコンテンツ -->
<Grid Background="MediumBlue" Width="300" Height="400">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock FontSize="30" Foreground="White" Margin="20,10">Flyoutの例</TextBlock>
<TextBox x:Name="flyoutTextBox" Grid.Row="1" FontSize="15" AcceptsReturn="True"
VerticalAlignment="Stretch" TextWrapping="Wrap" PlaceholderText="テキストボックス" ScrollViewer.VerticalScrollBarVisibility="Auto"
/>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="OK" Tapped="flyoutOkButton_Tapped" Width="100" FontSize="15" Background="White" />
<Button Content="キャンセル" Tapped="flyoutCancelButton_Tapped" Width="100" FontSize="15" Background="White" />
</StackPanel>
</Grid>
</Flyout>
</FlyoutBase.AttachedFlyout>
……省略……
</Grid>
このフライアウトを表示するためのコードビハインドは、次のようになる。
private void RootGrid_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
Flyout.ShowAttachedFlyout(sender as FrameworkElement);
}
Private Sub RootGrid_DoubleTapped(sender As Object, e As DoubleTappedRoutedEventArgs)
Flyout.ShowAttachedFlyout(DirectCast(sender, FrameworkElement))
End Sub
上ではShowAttachedFlyout静的メソッドを使ったが、ShowAtインスタンス・メソッドも使える(次のコード)。
this.Flyout1.ShowAt(pageTitle); // 引数は、フライアウトを付けるコントロール
Me.Flyout1.ShowAt(pageTitle) ' 引数は、フライアウトを付けるコントロール
MenuFlyoutコントロールはメニューを選択すると自動的に閉じる。Flyoutコントロールでは、ボタンがタッチ/クリックされたときの処理の中で明示的に閉じるコードを書く(次のコード)。
private void flyoutOkButton_Tapped(object sender, TappedRoutedEventArgs e)
{
this.Flyout1.Hide();
……省略(メニューに対応した処理)……
// 処理が終わったら、明示的にフライアウトを初期化しておく(後述)
this.flyoutTextBox.Text = string.Empty;
}
private void flyoutCancelButton_Tapped(object sender, TappedRoutedEventArgs e)
{
this.Flyout1.Hide();
}
Private Sub flyoutOkButton_Tapped(sender As Object, e As TappedRoutedEventArgs)
Me.Flyout1.Hide()
……省略(メニューに対応した処理)……
' 処理が終わったら、明示的にフライアウトを初期化しておく(後述)
Me.flyoutTextBox.Text = String.Empty
End Sub
Private Sub flyoutCancelButton_Tapped(sender As Object, e As TappedRoutedEventArgs)
Me.Flyout1.Hide()
End Sub
実行して、Gridコントロールをダブル・タップ/ダブル・クリックしてみよう。次の画像のようにフライアウトが出てくる。
適当な文字列を入力してから、フライアウト以外の画面をタップ/クリックしてフライアウトを閉じる。その後、再びフライアウトを出してみると、文字列もスクロール位置も以前のまま保持されている。フライアウトを閉じるメソッドの名前が「Close」ではなく「Hide」になっているのは、そういうことなのだ。画面から隠されるだけで、フライアウトのオブジェクトは存在し続けている。ユーザーが入力中に間違ってフライアウトを閉じてしまっても、また開けば続きを入力できるわけだ。コードの側では、[OK]ボタンのイベント・ハンドラで正しく処理が完了したら、明示的にフライアウトを初期化しなければならない。
Flyoutコントロール/MenuFlyoutコントロールはページのリソースとしても定義できる(次のコード)。この場合、個々のフライアウトのインスタンスに変数名を付けられないので、コードビハインドからフライアウトを出すにはShowAttachedFlyoutメソッドしか使えない。
<!-- ページのリソースにフライアウトを定義 -->
<Page.Resources>
……省略……
<MenuFlyout x:Key="MenuFlyout1">
<MenuFlyoutItem Text="メニュー1" Tapped="menuFlyout1_Tapped" />
<MenuFlyoutItem Text="メニュー2" Tapped="menuFlyout1_Tapped" />
</MenuFlyout>
</Page.Resources>
……省略……
<!-- 定義済みのフライアウトをボタンで利用 -->
<AppBarButton Icon="List" Label="MenuFlyout" Flyout="{StaticResource MenuFlyout1}" />
同じようにしてフライアウトをApp.xamlのリソースとして定義すれば、アプリのどこからでも使える。ただし、その場合はイベントが使えないため、ICommandオブジェクトをバインドして使う*3。
*3ICommandインターフェイスを実装したコマンド・オブジェクトをButtonコントロールのCommandプロパティにバインドするサンプル・コードを「Windows デベロッパー センター: ICommand インターフェイスを使って Button の Command を実行する」で公開しているので参考にしてほしい。
オプション設定パネルは、SettingsFlyoutコントロールを使ってXAMLでUIを定義する。VS 2013には、そのためのテンプレートが用意されているので、ソリューション・エクスプローラでプロジェクトを右クリックして(コンテキスト・メニューから)[追加]−[新しい項目]を選び、出てきたダイアログ(次の画像)で[ポップアップの設定](英語版では[Settings Flyout])を選んで追加する。
生成されたファイルは、XAMLエディタでUIを編集できる(次の画像)。ルート要素が<Page>や<UserControl>ではなく、<SettingsFlyout>になっていることに注目してほしい。
上部のタイトルやその右の小アイコンなどは、開始タグの属性で定義する(次のコード)。
<SettingsFlyout
x:Class=……省略……
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local=……省略……
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
IconSource="Assets/SmallLogo.png"
HeaderBackground="MediumBlue"
Title="オプション設定"
Width="320"
>
SettingsFlyoutコントロールのコンテンツには、自由にUIを定義する。例えば次のようにする。
<SettingsFlyout
……省略……
>
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<StackPanel Style="{StaticResource SettingsFlyoutSectionStyle}">
<TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="WinRT/Metro TIPS #54" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="0,0,0,25" TextWrapping="Wrap">
これはオプション設定パネルのサンプルです。
</TextBlock>
<Slider Foreground="Green" Background="Crimson" Value="50" Header="Slider" />
<ToggleSwitch Header="ToggleSwitch" />
</StackPanel>
</StackPanel>
</SettingsFlyout>
次に、設定チャームからこのSettingsFlyoutコントロールを呼び出せるようにする。こればかりは、ちょっと面倒なコードを書かなければならない。全ての画面に同じオプション設定パネルを追加するには、App.xaml.cs/.vbファイルに次のようなコードを記述する。
// Windowが作成されたタイミングでオプション設定パネルを出すためのハンドラを結び付ける
protected override void OnWindowCreated(WindowCreatedEventArgs args)
{
AddOptionPanel();
base.OnWindowCreated(args);
}
static internal void AddOptionPanel()
{
// [設定]チャームのインスタンスを取り出し、そこにイベント・ハンドラを結び付ける
Windows.UI.ApplicationSettings.SettingsPane.GetForCurrentView().CommandsRequested
+= SettingsPane_CommandsRequested;
}
// [設定]チャームが開くときのイベント・ハンドラ
static void SettingsPane_CommandsRequested(Windows.UI.ApplicationSettings.SettingsPane sender,
Windows.UI.ApplicationSettings.SettingsPaneCommandsRequestedEventArgs args)
{
Windows.UI.ApplicationSettings.SettingsCommand optionSetting
= new Windows.UI.ApplicationSettings.SettingsCommand(
"OptionSettings", // 識別子
"オプション設定", // 設定チャームに表示される文字列
(handler) =>
{
// オプション設定パネルを開く処理
SettingsFlyout1 optionsFlyout = new SettingsFlyout1();
optionsFlyout.Show();
});
args.Request.ApplicationCommands.Add(optionSetting);
}
' Windowが作成されたタイミングでオプション設定パネルを出すためのハンドラを結び付ける
Protected Overrides Sub OnWindowCreated(args As WindowCreatedEventArgs)
AddOptionPanel()
MyBase.OnWindowCreated(args)
End Sub
Friend Shared Sub AddOptionPanel()
' [設定]チャームのインスタンスを取り出し、そこにイベント・ハンドラを結び付ける
AddHandler _
Windows.UI.ApplicationSettings.SettingsPane.GetForCurrentView().CommandsRequested, _
AddressOf SettingsPane_CommandsRequested
End Sub
' [設定]チャームが開くときのイベント・ハンドラ
Private Shared Sub SettingsPane_CommandsRequested(sender As Windows.UI.ApplicationSettings.SettingsPane, _
args As Windows.UI.ApplicationSettings.SettingsPaneCommandsRequestedEventArgs)
Dim optionSetting As Windows.UI.ApplicationSettings.SettingsCommand _
= New Windows.UI.ApplicationSettings.SettingsCommand( _
"OptionSettings", _ ' 識別子(このコメントと次のコメントはコンパイル・エラーになるので入力してはいけない)
"オプション設定", _ ' 設定チャームに表示される文字列
Sub(handler)
' オプション設定パネルを開く処理
Dim optionsFlyout As SettingsFlyout1 = New SettingsFlyout1()
optionsFlyout.Show()
End Sub _
)
args.Request.ApplicationCommands.Add(optionSetting)
End Sub
コードビハインドから直接表示するのは、簡単だ(次のコード)。
SettingsFlyout1 optionsFlyout = new SettingsFlyout1();
optionsFlyout.Show();
Dim optionsFlyout As SettingsFlyout1 = New SettingsFlyout1()
optionsFlyout.Show()
SettingsFlyout1 optionsFlyout = new SettingsFlyout1();
optionsFlyout.ShowIndependent();
Dim optionsFlyout As SettingsFlyout1 = New SettingsFlyout1()
optionsFlyout.ShowIndependent()
実行すると次の画像のようになる。
Win 8.1で新設されたコントロールを使うと、フライアウトが簡単に実装できる。
フライアウトについては、次のドキュメントも参照してほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.