連載
» 2014年11月20日 09時43分 公開

WinRT/Metro TIPS:画面遷移の履歴を追加するには?[ユニバーサルWindowsアプリ開発]

セカンダリタイルからアプリを起動した場合などに、ページを楽に移動できるように、画面遷移の履歴を追加する方法を解説する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Platform Development]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

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

連載目次

 前回は、画面遷移の履歴を削除する方法を紹介した。逆に、画面遷移の履歴を追加/挿入したいことはないだろうか? 例えば、セカンダリタイルから起動したときに遷移先の画面を表示することがある。そのとき、[戻る]ボタンを表示してメイン画面に戻れるようにしたい、といった場合だ。そこで本稿では、画面遷移の履歴を追加する方法を解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #94」からダウンロードできる。

事前準備

 ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。

  • SLAT対応のPC*1
  • 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上*3
  • Visual Studio 2013 Update 2(またはそれ以降)*4を適用済みのVisual Studio 2013(以降、VS 2013)*5

*1 SLAT対応ハードウェアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウェアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。

*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。

*3 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。

*4 マイクロソフトのダウンロードページから誰でも入手できる(このURLはUpdate 4のもの)。

*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2013 with Update 4 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。また、この11月12日(米国時間)に新しくリリースされたVisual Studio Community 2013 with Update 4(製品版)もマイクロソフトのページから無償で入手できる。Communityエディションは本稿執筆時点では英語版だけなので、同じ場所にあるVisual Studio 2013 Language Packの日本語版を追加インストールする必要がある。


用語

 本稿では、紛らわしくない限り次の略称を用いる。

  • Windows:Windows 8.1とWindows RT 8.1(2014年4月のアップデートを適用済みのもの)
  • Phone:Windows Phone 8.1

サンプルコードについて

 Visual Studio 2013 Update 2(Update 3/4も)では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*6。そのため、本稿で紹介するコードはC#のユニバーサルプロジェクトだけとさせていただく*7

*6 VB用のユニバーサルプロジェクトは、来年にリリースされるといわれているVisual Studio 2015(開発コード「Visual Studio 14」)からの提供となるようだ。「Visual Studio UserVoice」(英語)のリクエストに対する、6月18日付けの「Visual Studio team (Product Team, Microsoft)」からの回答による。

*7 Visual Studio 2013 Update 2以降のVBでユニバーサルWindowsアプリを作る場合のお勧めは、「The Visual Basic Team」のブログ記事(英語)によれば、PCLを使う方法のようである。しかし、本稿で説明するような画面遷移関連のコードをPCLに置くことは非常に困難である(PCLから画面に直接アクセスできないためであり、絶対に不可能というわけではない)。


画面遷移の履歴を追加したいケース

 セカンダリタイルから起動できるアプリでは、メイン画面(=アプリ起動時に表示される画面)に戻れない(あるいは、戻る手順が複雑になる)場合がある。そこで画面遷移の履歴にメイン画面を追加できれば、[戻る]ボタンですぐにメイン画面に戻れるようになる。

 例えば、前回のサンプルコードでは、[メイン画面]→[画面A]→[画面B]→[画面C]→[メイン画面]という画面遷移を行った(エラー時の画面遷移は除く)。そこにセカンダリタイルから起動する機能を追加し、[画面A]を直接表示できるようにしてみよう(次の図)。すると、セカンダリタイルから起動したときの[画面A]は、Windowsでは[戻る]ボタンが表示されず、Phoneでは[戻る]ボタンのタップでスタート画面に戻ってしまう。

セカンダリタイルから[画面A]を直接開くと、[メイン画面]に戻るのが困難になる

セカンダリタイルから[画面A]を直接開くと、[メイン画面]に戻るのが困難になる
セカンダリタイルからの起動時、単純に[画面A]を表示させた場合。Windowsでは[画面A]に[戻る]ボタンが表示されず、Phoneでは[戻る]ボタンをタップするとスタート画面に戻ってしまう。「[メイン画面]から遷移してきた」という履歴がないためである。この例では[画面C]まで進めば[メイン画面]へ遷移するボタンが使えるが、そうでなければ[メイン画面]を表示する手段がなくなってしまう。
セカンダリタイルからの起動時に「[メイン画面]を表示した」という仮想的な履歴を追加できれば、この問題は解決するだろう。


 アプリからセカンダリタイルを作成できるようにするには、次のようなコードを記述する(このコードだけでは、作成したセカンダリタイルから起動しても[メイン画面]が表示されるだけである)。

private async void SetTile_Click(object sender, RoutedEventArgs e)
{
  // セカンダリタイルの生成
  // (簡易的に、固定IDのタイルを生成するだけ)
  const string TileId = "MetroTips094.PageA";
  if (!Windows.UI.StartScreen.SecondaryTile.Exists(TileId))
  {
    var secondaryTile 
      = new Windows.UI.StartScreen.SecondaryTile(
              tileId: TileId,
              displayName: "WinRT/Metro TIPS #094 Page-A",
              arguments: "PageA", // (1)
              square150x150Logo: new Uri("ms-appx:///Assets/Logo.png"),
              desiredSize: Windows.UI.StartScreen.TileSize.Square150x150
            );
    secondaryTile.VisualElements.BackgroundColor 
      = Windows.UI.Colors.Orange;
    secondaryTile.VisualElements.ForegroundText 
      = Windows.UI.StartScreen.ForegroundText.Dark;
    secondaryTile.VisualElements.ShowNameOnSquare150x150Logo = true;
    await secondaryTile.RequestCreateForSelectionAsync(
            GetElementRect(sender as FrameworkElement));
  }
}

public static Rect GetElementRect(FrameworkElement element)
{
  GeneralTransform buttonTransform = element.TransformToVisual(null);
  Point point = buttonTransform.TransformPoint(new Point());
  return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}

セカンダリタイルを作成するコード例(C#)
プロジェクトテンプレートで[ハブ アプリケーション (ユニバーサル アプリ)]を選んでプロジェクトを作成し、WindowsとPhoneのそれぞれのプロジェクトに「MainPage.xaml」(=メイン画面)と「PageA.xaml」(=画面A)を追加する。「App.xaml.cs」ファイルを編集して、起動時に「MainPage.xaml」が表示されるようにする。「MainPage.xaml」にボタンを配置し、そのクリックイベントで「PageA.xaml」に画面遷移させる。「PageA.xaml」には、セカンダリタイルを作成するためのボタンを配置し、そのイベントハンドラーとしてこのコードにある「SetTile_Click」メソッドを記述する(「GetElementRect」メソッドは、「SetTile_Click」メソッド内から呼び出しているメソッド)。
このコードは、共有プロジェクトに配置できる(=WindowsでもPhoneでも動作する)。別途公開のサンプルコードでは、コードビハインド全体を共有プロジェクトに置いている(共有プロジェクトの「View」フォルダー内)。
なお、セカンダリタイルのコンストラクターに渡したarguments引数の値(="PageA"、コメント(1))は、セカンダリタイルから起動されたかどうかの判定に使われる(後述)。

 上のコードで、スタート画面にセカンダリタイルを作成できるようになった。セカンダリタイルをタップしたときに[画面A]を表示する方法は、次で説明する。

画面遷移の履歴を追加するには?

 画面遷移の履歴は、Frameコントロール(Windows.UI.Xaml.Controls名前空間)のBackStackプロパティに保持されている。BackStackプロパティは、PageStackEntryオブジェクト(Windows.UI.Xaml.Navigation名前空間)のリストである。従って、PageStackEntryオブジェクトを生成してBackStackプロパティに追加すればよい。

 例えば、メイン画面(=「MainPage」クラス)を表示したという履歴をBackStackプロパティの末尾に追加するコードは、次のようになる。

rootFrame.BackStack.Add(new PageStackEntry(typeof(MainPage), null, null));

メイン画面を表示したという履歴を追加するコード(C#)
ここでrootFrame変数は、画面表示に使っているFrameクラス(Windows.UI.Xaml.Controls名前空間)のオブジェクトである。
PageStackEntryクラスのコンストラクターの引数のうち、1つ目は必須で、履歴として残したい画面の型を与える。残り2つの引数はオプションだが、2つ目は画面に渡すパラメーター、3つ目は画面遷移の際のアニメーションである。
なお、履歴の任意の位置に挿入することも可能で、それにはAddメソッドではなく、Insertメソッドを使う。

 あとは、上のコードの応用である。前述のコードで作成したセカンダリタイルから起動されたときに[画面A](=「PageA」クラス)を表示するコードは、次のようになる。

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
  ……省略……
#endif
  Frame rootFrame = Window.Current.Content as Frame;
  if (rootFrame == null)
  {
    ……省略……
  }

  // タイルのArgumentsを取り出す
  string launchArguments = string.Empty;
  if (e != null)
    launchArguments = e.Arguments;
  // セカンダリタイルがタップされたときはlaunchArgumentsの値が"PageA"になる

  if (rootFrame.Content == null)
  {
#if WINDOWS_PHONE_APP
    ……省略……
#endif

    if (launchArguments == "PageA")
    {
      // まだ画面が表示されていないときは、まず画面遷移する
      rootFrame.Navigate(typeof(PageA));
      // それから画面遷移履歴にメイン画面を追加する(順序が逆だとエラーになる)
      rootFrame.BackStack.Add(new PageStackEntry(typeof(MainPage), null, null));
      Window.Current.Activate();
      return;
    }

    if (!rootFrame.Navigate(typeof(MainPage), e.Arguments))
    {
      throw new Exception("Failed to create initial page");
    }
    // (1)
  }
  else
  {
    // すでに画面が表示されているとき
    if (launchArguments == "PageA")
    {
      // 現在の画面が[PageA]ではなかったら、画面遷移させる
      if (!(rootFrame.Content is PageA))
      {
        // すでに戻る先の画面は履歴にあるから、画面遷移させるだけでよい
        rootFrame.Navigate(typeof(PageA));
      }
    }
  }

  // 現在のウィンドウがアクティブであることを確認します
  Window.Current.Activate();
}

セカンダリタイルから[画面A]を表示するコード(C#)
共有プロジェクトにある「App.xaml.cs」ファイルのOnLaunchedメソッドに太字の部分を追加する。
セカンダリタイルでアプリが起動されたとき(=まだ画面を1つも表示していないとき)と、起動中にセカンダリタイルがタップされたとき(=すでに画面履歴が存在するとき)で、処理を切り分ける。前者の場合だけ、履歴を追加する(コード中のコメント参照)。
また、このコードには含めていないが、[画面B]や[画面C]を表示しているときにセカンダリタイルをタップされた場合は、[画面A]までさかのぼって履歴を消してから[画面A]を表示する、という設計も考えられる。
なお、OnActivatedメソッドのオーバーライドが必要な場合は、そちらにも同様のコードを記述する。別途公開のサンプルコードでは、共通する部分をOnLaunchedメソッドとOnActivatedメソッドからくくり出して1つのメソッドにまとめてある。
ちなみに、別解として、履歴を追加しない方法もある。実際に[メイン画面]を経由してから[画面A]を表示させても、同様な結果が得られるだろう。[メイン画面]を表示する処理が軽いならば、それでも構わない。その場合には、OnLaunchedメソッドで[メイン画面]へナビゲートしているコードの後ろ((1))に、[画面A]を表示するコードを追加すればよい。

まとめ

 画面遷移の履歴を追加するには、履歴を表すPageStackEntryオブジェクトを生成し、それをFrameコントロールのBackStackプロパティに追加すればよい。例として取り上げたセカンダリタイルの場合では、まだ履歴が1つもないときだけ履歴を追加するようにした。

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

WinRT/Metro TIPS

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

@IT Special

- PR -

TechTargetジャパン

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

RSSについて

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

メールマガジン登録

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