連載
» 2013年10月10日 14時16分 UPDATE

WinRT/Metro TIPS:簡単にフライアウトを出すには?[Windows 8.1ストア・アプリ開発]

Win 8アプリでは実装が面倒だったポップアップ。Win 8.1では新たに追加されたフライアウト表示用のコントロールを使うことでより手軽に実装できる。

[山本康彦(http://www.bluewatersoft.jp/),BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 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(ユーザー・インターフェイス)で、画面のほかの場所をタップ/クリックすれば閉じるものだ。フライアウトの使用例としては、[設定]チャームから呼び出すオプション設定パネルや、画面のオブジェクト上に出すショートカット・メニュー(=コンテキスト・メニュー、ポップアップ・メニュー)などがある(次の画像)。

ap-01.gif Win 8アプリのフライアウトの使用例
筆者が出しているアプリの1つで、オプション設定パネルとショートカット・メニューが表示される(両方同時に出ているのは画像合成)。
Win 8アプリでフライアウトを出すのは面倒だった。ショートカット・メニューには、コードビハインドでPopupMenuクラスのインスタンスを作りメニュー項目をコードで追加していた。オプション設定パネルはUIをXAMLで作れるのはよいのだが、コードビハインドでPopupクラスのインスタンスを作り、それにUIやイベント・ハンドラを結び付けるという手間を掛けていた*2

*2Win 8でのコード例は「MSDNブログ」の「XAMLでFlyout - Windows 8 ストアアプリテンプレート」などを参照してもらいたい。また、上の画像のアプリのコード(旧バージョン)を「クラウディアさんタイマー (Claudia Madobe Timer): XAML/C# [多言語化対応]」で公開している。


Win 8.1アプリでのフライアウト

 便利なフライアウトのコントロールがWindows.UI.Xaml.Controls名前空間に3つ追加された(次の表)。

コントロール 説明
Flyout 汎用的なフライアウトを作る
MenuFlyout ショートカット・メニューを作る。
PopupMenuクラスとは異なり、メニューはXAMLコード側に定義できる
SettingsFlyout オプション設定パネルを作る
Win 8.1アプリに追加された3つのフライアウト・コントロール

 本稿では、これらの使い方を説明していく。

Flyoutプロパティがあるコントロールでフライアウトを出すには?

 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>

MenuFlyoutを持つAppBarButtonコントロールの例(XAML)
このコードには、メニューを選択したときのイベント・ハンドラを追加する必要がある。

 この方法は前回「WinRT/Metro TIPS:アプリ・バーを簡単に実装するには?[Windows 8.1ストア・アプリ開発]」で紹介したので、そちらをご覧いただきたい。

Flyoutプロパティがないコントロールでフライアウトを出すには?

 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>

GridコントロールにFlyoutBase.AttachedFlyout添付プロパティを追加した(XAML)
フライアウトのコンテンツには、[OK]ボタン/[キャンセル]ボタンがある。また、後の説明のためにTextBoxコントロール(Windows.UI.Xaml.Controls名前空間)も入れてある。
また、Placementプロパティは、フライアウトの表示位置の指定だ。上のように「Bottom」を指定すると、対象のコントロールの下側に出そうとする。下側に出すスペースが無いときは残り3方向のいずれかに、それもだめならコントロールに重なって出る。

 このフライアウトを表示するためのコードビハインドは、次のようになる。

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

GridコントロールのDoubleTappedイベント・ハンドラで、添付されているフライアウトを出す(上:C#、下:VB)
簡潔なサンプルとして書いてある。実際は、引数senderが想定しているコントロールに正しくキャストできるかどうかチェックした方がよい。

 上ではShowAttachedFlyout静的メソッドを使ったが、ShowAtインスタンス・メソッドも使える(次のコード)。

this.Flyout1.ShowAt(pageTitle); // 引数は、フライアウトを付けるコントロール

Me.Flyout1.ShowAt(pageTitle) ' 引数は、フライアウトを付けるコントロール

コードビハインドの任意の箇所でフライアウトを出す(上:C#、下:VB)
フライアウトのインスタンスが利用できるなら、このように書ける。フライアウトの出る位置は、引数で与えたコントロールが基準になる。

Flyoutコントロール/MenuFlyoutコントロールの寿命

 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

コードビハインドでFlyoutコントロールを閉じる(上:C#、下:VB)
閉じるためのメソッド名が「Close」ではなく「Hide」であることに注目(実行結果のところで説明する)。

 実行して、Gridコントロールをダブル・タップ/ダブル・クリックしてみよう。次の画像のようにフライアウトが出てくる。

フライアウトの実行結果 フライアウトの実行結果
表示位置を「Bottom」と指定してあるが、スクリーン・キーボードは避けてくれる。ここで、テキストボックスにスクロールするだけの文字列を入力してからフライアウトを閉じて、また開いてみよう。

 適当な文字列を入力してから、フライアウト以外の画面をタップ/クリックしてフライアウトを閉じる。その後、再びフライアウトを出してみると、文字列もスクロール位置も以前のまま保持されている。フライアウトを閉じるメソッドの名前が「Close」ではなく「Hide」になっているのは、そういうことなのだ。画面から隠されるだけで、フライアウトのオブジェクトは存在し続けている。ユーザーが入力中に間違ってフライアウトを閉じてしまっても、また開けば続きを入力できるわけだ。コードの側では、[OK]ボタンのイベント・ハンドラで正しく処理が完了したら、明示的にフライアウトを初期化しなければならない。

Flyoutコントロール/MenuFlyoutコントロールをリソースとして定義するには?

 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}" />

MenuFlyoutコントロールをページのリソースとして定義して使用する例(XAML)
このようにFlyout属性に指定した場合は、ボタンのタップ/クリックで自動的にフライアウトが出る。
なお、<Page.Resources>タグがないときは、<Page>要素の開始タグの直後に追加する。

 同じようにしてフライアウトをApp.xamlのリソースとして定義すれば、アプリのどこからでも使える。ただし、その場合はイベントが使えないため、ICommandオブジェクトをバインドして使う*3

*3ICommandインターフェイスを実装したコマンド・オブジェクトをButtonコントロールのCommandプロパティにバインドするサンプル・コードを「Windows デベロッパー センター: ICommand インターフェイスを使って Button の Command を実行する」で公開しているので参考にしてほしい。


SettingsFlyoutコントロールでオプション設定パネルを作るには?

 オプション設定パネルは、SettingsFlyoutコントロールを使ってXAMLでUIを定義する。VS 2013には、そのためのテンプレートが用意されているので、ソリューション・エクスプローラでプロジェクトを右クリックして(コンテキスト・メニューから)[追加]−[新しい項目]を選び、出てきたダイアログ(次の画像)で[ポップアップの設定](英語版では[Settings Flyout])を選んで追加する。

SettingsFlyoutコントロールを使った画面定義を追加する SettingsFlyoutコントロールを使った画面定義を追加する
この画像のダイアログはC#のもので、[名前]はデフォルトの「SettingsFlyout1.xaml」のままにしておく。VBの場合は、[名前]を「SettingsFlyout1.xaml」に変更する。

 生成されたファイルは、XAMLエディタでUIを編集できる(次の画像)。ルート要素が<Page>や<UserControl>ではなく、<SettingsFlyout>になっていることに注目してほしい。

SettingsFlyoutコントロールを使った画面定義を編集する(XAMLエディタ) SettingsFlyoutコントロールを使った画面定義を編集する(XAMLエディタ)

 上部のタイトルやその右の小アイコンなどは、開始タグの属性で定義する(次のコード)。

<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コントロールのタイトル文字列/背景色/小アイコン/幅の定義(XAML)
幅の指定は、省略すると346px(設定チャームと同じ幅)になる。ビューの最小幅を320pxにしたときは、そのままでははみ出してしまうので、このように320pxに設定する。

 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コントロールのコンテンツの定義例(XAML)
データ・バインディングは省略してある。実際には、ここでのユーザー操作を、データ・バインディングを介してリアルタイムに設定に反映させる。詳細は「WinRT/Metro TIPS: ユーザー操作によるオプション設定を即座にデータに反映するには?[Win 8]」を参照してほしい。

 次に、設定チャームからこの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

オプション設定パネルを開くためのイベント・ハンドラを追加する処理を、Appクラスに追加する(上:C#、下:VB)
画面ごとに異なるオプション設定パネルを結び付ける場合は、それぞれのページのコンストラクタの最後あたりで上のAddOptionPanelメソッドを呼び出す(画面遷移時にハンドラを外すのを忘れないこと)。

 コードビハインドから直接表示するのは、簡単だ(次のコード)。

SettingsFlyout1 optionsFlyout = new SettingsFlyout1();
optionsFlyout.Show();

Dim optionsFlyout As SettingsFlyout1 = New SettingsFlyout1()
optionsFlyout.Show()

コードビハインドからオプション設定パネルを出す(その1)(上:C#、下:VB)
Showメソッドで表示した場合は、ユーザーが戻るボタンをタップ/クリックすると設定チャームに切り替わる。

SettingsFlyout1 optionsFlyout = new SettingsFlyout1();
optionsFlyout.ShowIndependent();

Dim optionsFlyout As SettingsFlyout1 = New SettingsFlyout1()
optionsFlyout.ShowIndependent()

コードビハインドからオプション設定パネルを出す(その2)(上:C#、下:VB)
ShowIndependentメソッドで表示した場合は、ユーザーが戻るボタンをタップ/クリックすればそのまま閉じる。

 実行すると次の画像のようになる。

出来上がったオプション設定パネルの動作(実行結果)
出来上がったオプション設定パネルの動作(実行結果)
出来上がったオプション設定パネルの動作(実行結果) 出来上がったオプション設定パネルの動作(実行結果)
画面を2分割して、左側に今回のアプリを表示した。
設定チャームに[オプション設定]が追加されている(上)。
設定チャームで[オプション設定]をタップ/クリックすると、オプション設定パネルが出る(中)。
ビューの幅が最小の状態でもWin 8.1アプリでは設定チャームが出せるので、オプション設定パネルも出る(下)。

まとめ

 Win 8.1で新設されたコントロールを使うと、フライアウトが簡単に実装できる。

 フライアウトについては、次のドキュメントも参照してほしい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。