ビューの切り替えを実装するには?[Win 8]WinRT/Metro TIPS

Windowsストア・アプリはデバイスを縦や横に持ち替えたときの向きに応じて適切な表示に切り替える必要がある。切り替わる4種類のビューと実装方法を解説。

» 2012年09月27日 00時00分 公開
[山本康彦BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider

powered by Insider.NET

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

連載目次はこちら

 Windowsストア・アプリ(旧称: Metroスタイル・アプリ)では、画面の向きなどに応じて適切な表示に切り替えねばならない。しかし、自動生成されたプロジェクト・テンプレートのコードを見ても、どのようにすれば実装できるのかが分かりづらいのではないだろうか? そこで本稿では、表示を切り替える方法を説明する。

事前準備

 Windows 8(以降、Win 8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿ではWin 8とVS 2012 Expressを使用している。

4つのビュー

 Windowsストア・アプリには、次の図のように4通りのビューを実装する。

Windowsストア・アプリの4通りのビュー
「FullScreenLandScape」「Filled」「Snapped」「FullScreenPortrait」は、ApplicationViewState列挙体(Windows.UI.ViewManagement名前空間)の値。
  (1)FullScreenLandScape(=横方向の全画面)。一般にはこれがデフォルト。
  (2)Filled(=ページ横幅に合わせたビュー)。画面横幅の4分の3ほどを使う。
  (3)Snapped(=スナップされたビュー)。画面横幅の4分の1ほどを使う(320ピクセル固定)。
  (4)FullScreenPortrait(=縦向きのビュー)。

ビューを切り替えたときに何が起きているのか?

 ユーザーがビューを切り替えると、SizeChangedイベントが発生する。そのときに、ApplicationViewクラス(Windows.UI.ViewManagement名前空間)のValue静的プロパティ(=ApplicationViewState列挙体の値)を調べると新しいビューが分かるので、ページの表示をそのビューに合わせて調整するのである。

 その調整時に、(XAMLコードではなく)C#/VBコードからページの表示を直接切り替えると、(切り替え後の表示はXAMLデザイナに反映されないので)初期状態以外のページのデザインは、実際にプログラムを実行して確認しないと分からないことになる。XAMLエディタ上で見ることはできないだろうか?

VisualStateとVisualStateManager

 そこで、VisualStateManagerクラス(Windows.UI.Xaml名前空間)(以降、単に「VisualStateManager」)を利用する。「コントロールの表示をどのように変更するか」ということをXAMLで定義できる(=「VisualState」と呼ばれる)ので、それをVisualStateManagerに渡し、表示を変えてもらうのだ。

 この方法はちょっと面倒な気もするが、実行しなくてもXAMLエディタでデザインを確認できるし、何よりも、SizeChangedイベント発生からVisualStateの適用まで、([グリッド アプリケーション(XAML)」や[分割アプリケーション(XAML)]などのプロジェクト・テンプレートによりひな型コードとして)自動生成されたLayoutAwarePageクラスが面倒を見てくれる。

[注意]ViewStateとVisualState

 この2つは紛らわしいが、次のとおり。

  • 「ViewState」は、「FullScreenLandScape」や「Snapped」などといった画面の状態
  • 「VisualState」は、コントロールが画面にどのように表示されているかという状態

VS 2012で4通りのビューを見るには?

 先に、4通りのVisualStateが作ってあるものとして、それをXAMLエディタ上で見る方法を説明しよう。

 XAMLエディタを表示している状態で、メニューバーから[デザイン]−[デバイス ウィンドウ]を選ぶと、次の画像のように[デバイス]ウィンドウが表示される。

[デバイス]ウィンドウとXAMLエディタ
左側にデバイス・ウィンドウが表示されている。赤枠内が[ビュー]オプション。

 [デバイス]ウィンドウの[ビュー]オプションに4つ並んだボタンで、XAMLエディタ内の表示を切り替えられる。

VisualStateを定義するには?

 [デバイス]ウィンドウには、[表示状態]オプションがある([ビュー]オプションの下)。これを使って、XAMLエディタでVisualStateを定義する。

  1. VisualStateを定義したいコントロールを選択する
  2. [ビュー]オプションで目的のビューに切り替える
  3. [表示状態]オプションの[状態記録の有効化]チェックボックスをONにする
  4. [プロパティ]ウィンドウを使って、コントロールの設定を変更する
  5. 完了したら、[状態記録の有効化]チェックボックスをOFFに戻す

 注意点は、4でXAMLコードを直接書き換えると失敗することと、プロジェクト・テンプレートで自動生成されるLayoutAwarePageクラスなどのファイルが必要なことだ。なお、慣れてきたら、XAMLコードを直接書き換えてもよい(書き換え方法は後述)。

4つの画面の切り替えを実装するには?

 説明用として、ごく単純化した例を紹介しよう。

 冒頭の画像の4通りのビューは、ページのタイトルは共通だが、色を付けた部分は別々のGrid(=レイアウト用のパネルの1つであるGridコントロールのこと。XAMLコード上では<Grid>要素)として作った。その手順を説明する。

 まず、新しいプロジェクトとして[新しいアプリケーション (XAML)]を作る。次に、そのプロジェクトに(新しいプロジェクト項目として)[基本ページ]を追加する(名前は「BasicPage1.xaml」とする)。このときにメッセージボックスが出てきて、「(前略)不足しているファイルを自動的に追加しますか?」と尋ねられるので[はい]と答えて、LayoutAwarePage.csファイルなどいくつかのファイルを自動生成させる。次に、App.xaml.cs/.vbファイルを開き、「MainPage」を探して、「BasicPage1」に書き換える。

 そうしたら、BasicPage1.xamlファイルのページ・タイトル定義部分の後ろに、次のコードのようにGridを4つ追加する。

<Grid>
  <!-- (省略 - 自動生成された[戻る]ボタンとページ・タイトル) -->
</Grid>

<!-- Full(=横置き全域)のときの画面定義 -->
<Grid x:Name="gridFull" Background="Blue" Grid.Row="1" Margin="120,0,0,80">
  <TextBlock Text="Full" FontSize="200" />
</Grid>
<!-- Fill(=横置き4分の3)のときの画面定義 -->
<Grid  x:Name="gridFilled" Background="Green" Grid.Row="1"
    Margin="120,0,0,80" Visibility="Collapsed">
  <TextBlock Text="Fill" FontSize="200"  />
</Grid>
<!-- Snap(=横置き4分の1)のときの画面定義 -->
<Grid x:Name="gridSnap" Background="Red" Grid.Row="1"
    Margin="0,0,0,80" Visibility="Collapsed">
  <TextBlock Text="Snap" FontSize="100" />
</Grid>
<!-- Portrait(=縦置き)のときの画面定義 -->
<Grid x:Name="gridPortrait" Background="Purple" Grid.Row="1"
    Margin="100,0,0,80" Visibility="Collapsed">
  <TextBlock Text="Portrait" FontSize="150" />
</Grid>


冒頭の画像に示した4つのGridのXAMLコード
Fullのとき以外の3つのGridは、Visibilityプロパティを「Collapsed」にして隠しておく。


 上記のコードに続く、<VisualStateManager.VisualStateGroups>要素の中身は、以下のXAMLコードで置き換える。なお、XAMLコードを直接編集したので、前述の方法で作った場合よりこれでも簡潔な記述になっている。

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup x:Name="ApplicationViewStates">
    <!-- Full(=横置き全域)を表示するときのアニメーション定義 -->
    <VisualState x:Name="FullScreenLandscape">
      <!-- (なし) -->
    </VisualState>
    <!-- Fill(=横置き4分の3)を表示するときのアニメーション定義 -->
    <VisualState x:Name="Filled">
      <Storyboard>
        <!-- Fullは隠す -->
        <ObjectAnimationUsingKeyFrames
            Storyboard.TargetProperty="(UIElement.Visibility)"
            Storyboard.TargetName="gridFull">
          <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
        </ObjectAnimationUsingKeyFrames>
        <!-- Fillを表示する -->
        <ObjectAnimationUsingKeyFrames
            Storyboard.TargetProperty="(UIElement.Visibility)"
            Storyboard.TargetName="gridFilled">
          <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </VisualState>
    <!-- Snap(=横置き4分の1)を表示するときのアニメーション定義 -->
    <VisualState x:Name="Snapped">
      <Storyboard>
        <!-- [戻る]ボタンとタイトルは、Snap用の小さめスタイルに変える -->
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
            Storyboard.TargetProperty="Style">
          <DiscreteObjectKeyFrame KeyTime="0"
            Value="{StaticResource SnappedBackButtonStyle}"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle"
            Storyboard.TargetProperty="Style">
          <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource
            SnappedPageHeaderTextStyle}"/>
        </ObjectAnimationUsingKeyFrames>
        <!-- Fullは隠す -->
        <ObjectAnimationUsingKeyFrames
            Storyboard.TargetProperty="(UIElement.Visibility)"
            Storyboard.TargetName="gridFull">
          <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
        </ObjectAnimationUsingKeyFrames>
        <!-- Snapを表示する -->
        <ObjectAnimationUsingKeyFrames
            Storyboard.TargetProperty="(UIElement.Visibility)"
            Storyboard.TargetName="gridSnap">
          <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </VisualState>
    <!-- Portrait(=縦置き)を表示するときのアニメーション定義 -->
    <VisualState x:Name="FullScreenPortrait">
      <Storyboard>
        <!-- [戻る]ボタンは、Portrait用の小さめスタイルに変える -->
        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
            Storyboard.TargetProperty="Style">
          <DiscreteObjectKeyFrame KeyTime="0"
            Value="{StaticResource PortraitBackButtonStyle}"/>
        </ObjectAnimationUsingKeyFrames>
        <!-- Fullは隠す -->
        <ObjectAnimationUsingKeyFrames
            Storyboard.TargetProperty="(UIElement.Visibility)"
            Storyboard.TargetName="gridFull">
          <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
        </ObjectAnimationUsingKeyFrames>
        <!-- Portraitを表示する -->
        <ObjectAnimationUsingKeyFrames
            Storyboard.TargetProperty="(UIElement.Visibility)"
            Storyboard.TargetName="gridPortrait">
          <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </VisualState>
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>


冒頭の画像に示した4通りを切り替えるVisualStateのXAMLコード
4つのビューごとにVisualState(=VisualStateオブジェクト)が定義してある。それぞれの中で、Gridの表示/非表示を切り替えている。なお、[戻る]ボタンとタイトルに関する部分は、自動生成されたXAMLコードのまま使っている。


 VisualStateごとにGridの表示/非表示を切り替えている。各VisualStateには、「FullScreenLandscape」という名前のVisualStateからどのように変化させるのか、ということを定義する。

 なお、「FullScreenLandscape」と「Filled」のVisualStateで共通のコントロールを使い、Marginプロパティだけを変更する、といったようなことも可能だ。実際、この2つのビューは、コントロールを共通にできることが多い。

もっと複雑な画面切り替えは?

 プロジェクト・テンプレート[分割アプリケーション (XAML)]のSplitPage.xamlファイルを見てもらいたい。デバイス・ウィンドウで縦向きのビューに切り替えてから、[表示状態]オプションのドロップダウンで[FullScreenPortrait_Detail]を選択すると、同じ縦向きのビューではあるが表示が変わる。横向きでは1画面に収まっていたビューを、縦向きのときは左右2画面に分割しているのだ。このような標準外のVisualStateを実装するには、LayoutAwarePageクラスのDetermineVisualStateメソッドもオーバーライドする必要がある。

まとめ

 ビューの切り替えを実装するには、「FullScreenLandscape」という名前のVisualStateから表示を変更するために必要なプロパティの設定をVisualStateに定義する。

 ビューの設計や実装については、次のリンク先のドキュメントが参考になる。

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

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

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

メールマガジン登録

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