連載
» 2015年01月08日 15時30分 UPDATE

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ランタイムアプリ*1では、中断時にデータを保存すべしという。では、実際にはどのようにすればよいのだろうか? 以前にも説明したことはあるが、本稿ではあらためてユニバーサルプロジェクトの場合について解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #97」からダウンロードできる。

*1 このMSDNのWebページは、2014年12月下旬に改訂された。以前は「ストアアプリ」と記述されていた部分が「Windowsランタイムアプリ」に変更されている。


事前準備

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

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

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

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

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

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

*6 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2013 with Update 4 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。また、2014年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用のユニバーサルプロジェクトのテンプレートは含まれていない*7。そのため、本稿で紹介するコードはC#のユニバーサルプロジェクトだけとさせていただく。

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


アプリのライフサイクルと保存すべきデータ

 Windowsランタイムアプリのライフサイクルは、次の図のように説明される。

Windowsランタイムアプリのライフサイクル Windowsランタイムアプリのライフサイクル
詳細は特集記事「Metroスタイル・アプリの開発者が知るべき3つのこと」を参照してほしい。
この図には表現していないが、実行中に異常終了(例外の発生や長時間無応答など)した場合は左側の「実行していない(初期状態)」へ遷移する。
正常に終了して右側の「実行していない」状態になった後で次に起動(=アクティブ化)されるときは、中断した状態を復元すべきだ。すると、状態を復元するために必要なデータは、中断されるときに保存する必要がある。なぜなら、アプリが中断状態から終了させられるときには、アプリには何もできないからである。この中断時にデータを保存し、それを次のアクティブ化時に復元するにはどうすればよいかが、本稿の主題である。

 上の図で、アプリが正常に終了した後で次に起動(=アクティブ化)されるときには、中断したときの状態を復元するとよい(過去の認定要件の3.6項では義務付けられていた。2014年1月23日付の改訂で廃止)。中断状態からアプリが終了させられるときには、アプリには何もできない。従って、中断したときの状態を復元するためのデータは、それ以前に保存しておかねばならない。一般的には、中断されるときに保存する。本稿では、中断されるときにデータを保存する方法と、次に起動されるときにデータを復元する方法を説明する。

 ところで、アプリが保存すべきデータには2種類ある。強制終了時に失ってもよいデータと、強制終了時であっても失っては困るデータだ。上の図で、「終了させる」操作をしたときや、例外を処理できずに異常終了したとき、あるいは長時間無応答などでシステムから強制的に終了させられたときなどに、失っても構わないデータかどうかで判断する。強制終了されたアプリが次に起動されるときには状態を初期化して起動すればよい(すなわち、中断時の状態を復元するためのデータを使わずに起動すればよい)のだから、中断時の状態を復元するためのデータは失っても構わない。しかし、ユーザーが保存したと考えているデータは失ってはいけないだろう。本稿では、前者を「セッションデータ」、後者を「ユーザーデータ」と呼ぶことにする(次の表)。以下ではこのうち「セッションデータ」の保存と復元を行う方法を見ていく。

データ区分 意味 喪失 保存タイミング
セッションデータ アプリが中断された時の状態を復元するためのデータ 強制終了時には失われてもよい 中断時
ユーザーデータ それ以外(ユーザーが保存したと考えているデータ) どんなときでも失われるのは望ましくない データが生成/変更される都度
セッションデータとユーザーデータ
本稿で考えるのはセッションデータの保存と復元である。ユーザーデータは、データが生成/変更されたときに可能な限り速やかに保存すべきだ。

SuspensionManagerクラスの利用

 中断時にセッションデータを保存するのに適した場所は二つある。ローカルフォルダーとローミングフォルダーだ*8。そして、ローカルフォルダーに保存する場合には、VS 2013のプロジェクトテンプレートで提供されているSuspensionManagerクラスを利用できる。SuspensionManagerクラスを使えば実装が簡単になるのだ。本稿では、その方法を説明する。その他の方法も併せて、次の表にまとめておく。

保存先 セッションの範囲 実装手段
ローカルフォルダー デバイスごと(別のデバイスでは中断時の状態を復元しない) SuspensionManagerクラスを利用
または
独自に実装
ローミングフォルダー アプリごと(別のデバイスでも中断時の状態を復元する) 独自に実装
セッションデータの保存先と実装手段
ローミングフォルダーに保存する場合、他のデバイスで保存されたデータがローミングされてきたときのイベントを処理する必要もある。その設計と実装は少々難しい。「WinRT/Metro TIPS:WindowsとPhoneで設定をローミングするには?[ユニバーサルWindowsアプリ開発]」を参照。また、ユニバーサルWindowsアプリではWindowsで中断した状態をPhoneで復元する(あるいはその逆も)必要があるのか、という問題もある。

*8 アプリケーションデータ記憶域の種類は「WinRT/Metro TIPS:アプリケーション・データ記憶域のテキスト・ファイルを読み書きするには?[Win 8/WP 8]」を参照。その中で一時フォルダーは、次に起動されるまでの間にファイルが消されてしまう可能性があるので、セッションデータの保存先としてはふさわしくない。なお、ネットワークに保存するのは、中断時に許された処理時間(=5秒)では保存に失敗する可能性が大きいためお勧めできない。


SuspensionManagerクラスを使う準備

 本稿では、VS 2013の[ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートから始めよう。このプロジェクトテンプレートには、SuspensionManagerクラスが含まれている。新しくプロジェクトを作るときにこのテンプレートを選ぶと、生成されたプロジェクトのCommonフォルダーにSuspensionManagerクラスがある(次の画像)。

[ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートを選んでプロジェクトを作る(VS 2013)
[ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートを選んでプロジェクトを作る(VS 2013) [ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートを選んでプロジェクトを作る(VS 2013)
:メニューバーの[ファイル]−[新規作成]−[プロジェクト]を選ぶと、この[新しいプロジェクト]ダイアログが出てくる。左側で、[インストール済み]−[テンプレート]−[Visual C#]−[ストア アプリ]−[ユニバーサル アプリ]を選び、中央で[ハブ アプリケーション (ユニバーサル アプリ)]を選ぶ。下でプロジェクト名や保存場所などを指定し、[OK]ボタンをクリックしてプロジェクトを作成する。
:自動生成されたプロジェクトをソリューションエクスプローラーで見ると、共有プロジェクト(プロジェクト名の末尾が「.Shared」になっている)の下のCommonフォルダーにSuspensionManagerクラスがある。また、SuspensionManagerクラスを利用するための基本的なコードが、自動生成されたAppクラスに実装されている。

 なお、プロジェクトを作るときに[空のアプリケーション (ユニバーサル アプリ)]を選んだ場合にはCommonフォルダーが作成されない。その後、Windows用のプロジェクト(プロジェクト名の末尾が「.Windows」)かPhone用のプロジェクト(プロジェクト名の末尾が「.WindowsPhone」)に画面を追加するときに、[空白のページ]を除く[基本ページ]などを選ぶと、Commonフォルダーが作成される(画面を追加したプロジェクトに作成されるが、そのCommonフォルダーを丸ごと共有プロジェクトにカット&ペーストする)。ただしその場合には、AppクラスにSuspensionManagerクラスを使うためのコードが入っていないので、[ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートで作られた「App.xaml.cs」ファイルを参考にしてコードを修正する必要がある。

 プロジェクトが作成できたら、次の画像とコードで示すようなUIを作っておいてほしい。後のコードで、上のテキストボックスに入力した文字列をセッションデータとして保存/復元する。下のテキストボックスには、セッションデータとして保存しておいた中断時刻を表示する。

後のコードで使用するUI(上:Windows、下:Phone)
後のコードで使用するUI(上:Windows、下:Phone) 後のコードで使用するUI(上:Windows、下:Phone)

<StackPanel DataContext="{Binding DataContext, ElementName=pageRoot}">
  <TextBox Header="TextBox1" Width="200" HorizontalAlignment="Left"
            Text="{Binding TextBox1Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
  <StackPanel Orientation="Horizontal" Margin="0,40,0,0">
    <TextBox Header="前回中断時刻" IsReadOnly="True" Width="150" Background="Transparent"
              BorderBrush="LightGray" Text="{Binding LastSuspnededTime}"
              HorizontalAlignment="Left" />
    <AppBarButton Icon="Refresh" Click="RefreshButton_Click" IsCompact="True" />
  </StackPanel>
</StackPanel>

後のコードで使用するUIのコード例(XAML)
先の画像の赤枠内のコード。自動生成された「HubPage.xaml」ファイルに追加した。WindowsとPhoneで共通である。 この他に、Phoneではファイル冒頭の<Page>要素に「x:Name="pageRoot"」という記述を追加した(Windowsに合わせた)。 ボタンのクリックイベントを書いたら、そのイベント名「RefreshButton_Click」のところで[F12]キーを押して、メソッドスタブを生成しておく。

 最後に、テキストボックスの初期値をセットするコードを追加しておく(次のコード)。

private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
  ……省略(自動生成されたコード)……

  // DefaultViewModelの初期値をセット
  this.DefaultViewModel["TextBox1Text"] = string.Empty;  this.DefaultViewModel["LastSuspnededTime"] = string.Empty;
}

テキストボックスの初期値をセットするコード例(C#)
画面「HubPage.xaml」のコードビハインドにあるNavigationHelper_LoadStateメソッドに、太字の部分を追加する。 省略した部分(プロジェクトテンプレートで自動生成された部分)はWindowsとPhoneで異なるが、追加するコードは同じである。

画面ごとのセッションデータを保存/復元するには?

 画面のコードビハインドに保存/復元するコードを記述すればよい。NavigationHelper_SaveStateメソッドで保存し、NavigationHelper_LoadStateメソッドで復元する。以下、処理の流れを追いながら、追加すべきコードを説明していく。

中断イベントを受信

 プロジェクトテンプレートで自動生成されたAppクラスのコンストラクターでは、そのSuspendingイベントハンドラーとしてOnSuspendingメソッドが登録されている(次のコード)。これにより、システムから「中断状態に入れ」というイベント(=Suspendingイベント)が送られてくると、OnSuspendingメソッドが実行される。

public App()
{
  this.InitializeComponent();
  this.Suspending += this.OnSuspending;
}

自動生成されたAppクラスのコンストラクター(C#)
SuspendingイベントハンドラーとしてOnSuspendingメソッドが登録されている(太字の部分)。

OnSuspendingメソッド

 自動生成されたOnSuspendingメソッドは、次のコードのようになっている。その中で呼び出しているSuspensionManagerクラスのSaveAsyncメソッドが、各画面のNavigationHelper_SaveStateメソッドを呼び出した後にセッションデータをファイルに保存してくれるのだ。

private async void OnSuspending(object sender, SuspendingEventArgs e)
{
  var deferral = e.SuspendingOperation.GetDeferral();

  // テンプレートで生成されたコードのSaveAsyncメソッドは、各画面のNavigationHelper_SaveStateメソッドを呼び出し、
  // それからローカルフォルダーの「_sessionState.xml」ファイルにセッションデータを書き出してくれる
  await SuspensionManager.SaveAsync();

  // テンプレートに記述されたGetDeferral〜deferral.Completeは、
  // 非同期処理の完了を待つようにシステムへ指示する
  deferral.Complete();
}

自動生成されたAppクラスのOnSuspendingメソッド(C#)
SaveAsyncメソッド(太字)は、各画面のNavigationHelper_SaveStateメソッドを呼び出した後にセッションデータをファイルに保存してくれる。
メソッド冒頭の「e.SuspendingOperation.GetDeferral()」は、「これから非同期処理を行うので、その完了報告を待つように」とシステムに伝える。これを忘れると、SaveAsyncメソッドが走り始めた直後くらいに中断状態に入ってしまう。非同期処理が完了したら、メソッド末尾にあるように「deferral.Complete()」を呼び出して、システムに完了を通知する。そうすれば、きちんと非同期処理が完了してから中断状態に入る。ただし、OnSuspendingメソッドに許された時間は現在のところ5秒間であり、それを超えると処理が終わっていなくても強制的に中断されるので気を付けてほしい。

 なお、OnSuspendingメソッドに許された時間は現在のところ5秒間である。処理に使える残り時間は、次のコードのようにして取得できる。この時間を超えると、処理中であっても強制的に中断されるので気を付けてほしい。

private async void OnSuspending(object sender, SuspendingEventArgs e)
{
  var deferral = e.SuspendingOperation.GetDeferral();

  // 許された残り時間は次のコードで確認できる
  var remainTime = e.SuspendingOperation.Deadline.Subtract(DateTimeOffset.Now);
  // → remainTime.TotalSeconds=4.9970003秒(Windows)/4.9800024秒(Phone)

  await SuspensionManager.SaveAsync();
  deferral.Complete();
}

OnSuspendingメソッドに許された残り時間を取得する例(C#)
コメントに書いた秒数は実行例だ。現在のところ、Windows/Phoneとも5秒間である(将来のバージョンで変わる可能性がある)。

NavigationHelper_SaveStateメソッド

 各画面のNavigationHelper_SaveStateメソッドで、画面ごとのセッションデータを保存する(正確には、ここではSuspensionManagerクラスにデータを渡すだけであり、実際にファイルへ保存されるのはNavigationHelper_SaveStateメソッドを抜けてからである)。

 ここでは、テキストボックスにエンドユーザーが入力した文字列を、「TextBox1Text」というキー名で保存してみよう(次のコード)。

private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
  e.PageState["TextBox1Text"] = this.DefaultViewModel["TextBox1Text"] as string;
}

画面のNavigationHelper_SaveStateメソッドでセッションデータを保存する(C#)
ここで画面ごとのセッションデータを保存する(太字の部分)。引数eのPageStateプロパティに、キー名を指定して保存したい値をセットするだけだ。
このコードは「HubPage.xaml」画面のコードビハインドに記述した。Windowsでは、このメソッド全体を追加する。Phoneではすでにメソッドが存在するので、メソッドの内容だけ(太字の部分)を追加する。
なお、このメソッド内では非同期処理を呼び出してはいけない。SuspensionManagerクラスのSaveAsyncメソッドは非同期処理の完了を待ってくれないので、このメソッドで非同期処理を呼び出した直後にファイルへ書き出してしまう。
また、PageStateプロパティに格納できるオブジェクトは、既定ではXMLにシリアライズできる型に限られているので、注意してほしい(それ以外の型でも保存できなくはないが、かなり面倒になる)。

 なお、自動生成されたWindows用のコードビハインドにはNavigationHelper_SaveStateメソッドが記述されていない。上のコードを追加するとともに、次のコードのようにコンストラクターでイベントハンドラーを登録する必要がある。

public HubPage()
{
  this.InitializeComponent();
  this.navigationHelper = new NavigationHelper(this);
  this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
  this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
}

Windowsでは画面のコンストラクターでNavigationHelper_SaveStateメソッドを登録する(C#)
太字の部分を追加した。
Phoneでは、プロジェクトテンプレートで自動生成されたコードにすでに記述されている。

セッションデータが保存される

 以上で、テキストボックスに入力した文字列がセッションデータとして中断時に保存されるようになったはずである(復元はまだ)。デバッグ実行して、文字列をキーインし、中断からシャットダウンしてみてほしい(次の画像)。

デバッグ実行でセッションデータが保存されることを確認する(Windows)
デバッグ実行でセッションデータが保存されることを確認する(Windows)
デバッグ実行でセッションデータが保存されることを確認する(Windows) デバッグ実行でセッションデータが保存されることを確認する(Windows)
:デバッグ実行を開始し、テキストボックス(赤丸内)に何か文字列を入力する。ここでは「試験」と入力した。
:続いて、VS 2013のIDEに戻って[中断とシャットダウン]をシミュレートする。この画像の[ライフサイクル イベント]ドロップダウンが表示されていない場合は、ツールバーの上で右クリックし、そのコンテキストメニューで[デバッグの場所]を選ぶと表示される。
:中断するとローカルフォルダーに「_sessionState.xml」ファイルができているので、「メモ帳」などで内容を確認する。キー名「TextBox1Text」とそのデータ「試験」が記録されているのが分かるだろう(赤枠内)。ローカルフォルダーの実際のパスは、Windows.Storage.ApplicationData.Current.LocalFolder.Pathで取得できる。
なお、Phoneでも同じであるが、ローカルフォルダー内のファイルを確認するのは手間なので、ここでは割愛する。

中断からシャットダウンした後でアプリを起動

 中断からシャットダウンした後でアプリを起動すると、AppクラスのOnLaunchedメソッドの中でSuspensionManagerクラスのRestoreAsyncメソッドが呼び出される(次のコード)。この部分は、プロジェクトテンプレートで自動生成されたコードだ。

 SuspensionManagerクラスのRestoreAsyncメソッドは、画面ごとにセッションデータを復元し、画面のNavigationHelper_LoadStateメソッドを呼び出してくれる(その他に、画面の遷移履歴も復元する)。

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
  ……省略……
  if (rootFrame == null)
  {
    rootFrame = new Frame();
    ……省略……
    if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
    {
      // 必要な場合のみ、保存されたセッション状態を復元します
      try
      {
        await SuspensionManager.RestoreAsync();
      }
      catch (SuspensionManagerException)
      {
      }
    }
    Window.Current.Content = rootFrame;
  }

  if (rootFrame.Content == null)
  {
    // ナビゲーションの履歴スタックが復元されていない場合、最初のページに移動します。
    ……省略……
  }
  Window.Current.Activate();
}

自動生成されたAppクラスのOnLaunchedメソッド(C#)
前回、正常に中断からシャットダウンされた場合(=引数eのPreviousExecutionStateプロパティの値がApplicationExecutionState.Terminatedのとき)に限って、SuspensionManagerクラスのRestoreAsyncメソッドが呼び出される。
RestoreAsyncメソッドは、画面ごとのセッションデータを復元し、そのデータを引数に入れて画面のNavigationHelper_LoadStateメソッドを呼び出す。

NavigationHelper_LoadStateメソッド

 画面のNavigationHelper_LoadStateメソッドの第2引数には、復元したセッションデータが入っている(復元したセッションデータがある場合)。従って、次のコードのようにして引数からセッションデータを取り出して画面にセットすればよい。

private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
  ……省略(自動生成されたコード)……

  // DefaultViewModelの初期値をセット
  this.DefaultViewModel["TextBox1Text"] = string.Empty;
  this.DefaultViewModel["LastSuspnededTime"] = string.Empty;

  // 画面ごとのセッションデータは、次のようにして取り出す
  if (e.PageState != null)
  {
    if (e.PageState.Keys.Contains("TextBox1Text"))
    {
      this.DefaultViewModel["TextBox1Text"] = e.PageState["TextBox1Text"] as string;
    }
  }
}

画面のNavigationHelper_LoadStateメソッドでセッションデータを復元する(C#)
画面「HubPage.xaml」のコードビハインドにあるNavigationHelper_LoadStateメソッドに、太字の部分を追加する。
引数eのPageStateプロパティに、NavigationHelper_SaveStateメソッドで保存したセッションデータが入ってくる。それを取り出して画面を復元する。

 以上で、セッションデータの復元も完了だ。実行して、テキストボックスに何か文字列を入力した状態で中断からシャットダウンをし、再び起動したときに以前の文字列が表示されることを確認してほしい。

 処理の流れは複雑だったが、実際に記述したのはNavigationHelper_SaveStateメソッドでデータをセットするコードと、NavigationHelper_LoadStateメソッドでデータを取り出すコードの2カ所だけであった(WindowsではSaveStateイベントハンドラーを登録するコードも記述した)。[ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートを使うと、セッションデータの保存/復元が簡単に記述できるようになっているのである。

 なお、NavigationHelper_SaveStateメソッドは、画面遷移するときにも呼び出される。そのときはPageStateオブジェクトにセッションデータが保持されるだけで、ファイルには保存されない(画面遷移した先で中断されたときには、ファイルに保存される)。

アプリ全体のセッションデータを保存/復元するには?

 画面によらず、アプリとして保存/復元したいセッションデータは、どこで処理すればよいだろうか? 保存は、自動生成されたAppクラスのOnSuspendingメソッドで行えばよい。復元は、AppクラスのOnLaunchedメソッド内でSuspensionManagerクラスのRestoreAsyncメソッドを呼び出した後なら、どこで行ってもよい。

 しかし、OnSuspendingメソッドでセッションデータを保存しようにも、画面でやったときのようにPageStateオブジェクトにアクセスできない。どこに保存すればよいだろうか? 画面のセッションデータが保存される「_sessionState.xml」ファイルへ一緒に保存するには、SuspensionManagerクラスのSessionStateプロパティを利用する(独自のファイルに保存してもよいが、その場合はSuspensionManagerクラスのRestoreAsyncメソッドで復元してもらえない)。

 それでは実際のコード例を紹介しよう。ここでは、中断されたときの時刻をセッションデータとして保存してみる(次のコード)。注意点は、保存するキー名として「AppFrame」は使えないことと、SuspensionManagerクラスのSaveAsyncメソッドを呼び出す前に行わなければならないことだ。

private async void OnSuspending(object sender, SuspendingEventArgs e)
{
  var deferral = e.SuspendingOperation.GetDeferral();

  // SuspensionManager.SessionStateのキー"AppFrame"にはページごとのセッションデータが入っている
  // アプリ全体のセッションデータは、それとは別のキーで保存する
  SuspensionManager.SessionState["SuspendTime"] = DateTimeOffset.Now.ToString("HH:mm:ss");

  await SuspensionManager.SaveAsync();
  deferral.Complete();
}

アプリ全体のセッションデータを保存するコード例(C#)
自動生成されたAppクラスのOnSuspendingメソッドに、太字の部分を追加した。
ここでは、中断されたときの時刻をセッションデータとして保存している。
SuspensionManagerクラスのSessionStateプロパティにセットしたデータは、SuspensionManagerクラスのSaveAsyncメソッドの中でファイルに書き出される(呼び出す順序を逆にするとファイルに書き出されないので注意してほしい)。
また、保存するキー名として「AppFrame」は使えない。自動生成されたAppクラスのOnLaunchedメソッドの中で、すでに「AppFrame」という名前が使われているからだ。
なお、ここで保存できるデータも、XMLにシリアライズできる型に限られている。

 このセッションデータは、SuspensionManagerクラスのRestoreAsyncメソッドを呼び出すと復元されるので、その後ならどこからでも取り出せる。例えば、画面のNavigationHelper_LoadStateメソッドで次のコードのように記述できる。

private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
  ……省略(自動生成されたコード)……

  // DefaultViewModelの初期値をセット
  this.DefaultViewModel["TextBox1Text"] = string.Empty;
  this.DefaultViewModel["LastSuspnededTime"] = string.Empty;

  // 画面ごとのセッションデータは、次のようにして取り出す
  if (e.PageState != null)
  {
    if (e.PageState.Keys.Contains("TextBox1Text"))
    {
      this.DefaultViewModel["TextBox1Text"] = e.PageState["TextBox1Text"] as string;
    }
  }

  // アプリ全体のデータは、SuspensionManager.SessionStateから直接取り出す
  if (SuspensionManager.SessionState.Keys.Contains("SuspendTime"))
    this.DefaultViewModel["LastSuspnededTime"]
      = SuspensionManager.SessionState["SuspendTime"] as string;
}

画面のNavigationHelper_LoadStateメソッドで、アプリ全体の復元されたセッションデータを表示するコード例(C#)
太字の部分を追加した。これで、最後に中断した時刻が、画面表示時に表示される。
このセッションデータは、画面によらず、中断したときに必ず保存されるデータである。従って、他の画面に遷移し、そこで中断/再開し、その後でこの画面に戻ってきた場合でも中断したときの時刻が表示される。

 あるいは、ボタンのクリックイベントで取り出してもよい(次のコード)。

private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
  if (SuspensionManager.SessionState.Keys.Contains("SuspendTime"))
    this.DefaultViewModel["LastSuspnededTime"
      = SuspensionManager.SessionState["SuspendTime"] as string;
}

画面のボタンクリックイベントで、アプリ全体の復元されたセッションデータを表示するコード例(C#)
上と同じコードを、イベントハンドラーのメソッドに記述しただけである。

まとめ

 セッションデータを保存/復元する処理は、結構複雑だ。しかし、VS 2013の[ハブ アプリケーション (ユニバーサル アプリ)]プロジェクトテンプレートにはその複雑な部分が組み込まれているので、画面ごとのセッションデータの保存/復元は、NavigationHelper_SaveStateメソッドでデータをセットするコードと、NavigationHelper_LoadStateメソッドでデータを取り出すコードを記述するだけで済む。また、画面によらないセッションデータの保存は、自動生成されたAppクラスのOnSuspendingメソッドの中で行える。

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

WinRT/Metro TIPS

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

@IT Special

- PR -

TechTargetジャパン

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

Focus

- PR -

RSSについて

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

メールマガジン登録

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