ViewModelのTombstonedの処理について

Windows Phone SDK 7.1からTombstonedに加えDormantという状態が加わったためページを復元する処理がややこしくなりました。そこでViewModelを保存し復元するときの注意点をまとめてみました。

その前にTombstonedやDormantについて知りたい方はこちらの記事を参考にしてください。
Tombstoned(トゥームストーン)とDormant(ドーマント)について


Tombstonedのときはアプリケーションとページの2つの状態を保存し復元することを考慮する必要がありますが、ここではページの状態についての説明します。

保存のタイミング

OnNavigatedFromメソッドでページを離れるときにページの状態を保存します。

private ViewModel _viewModel;
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
    {
        State["ViewModel"] = _viewModel;
    }
}

SDK 7.1からOnNavigatedToとOnNavigatedFromの引数に渡ってくるNavigationEventArgsにNavigationModeが追加されました。これはナビゲーションの種類を表します。

NavigationMode 説明
New 新規にページが開かれるとき(NavigateメソッドやSourceメソッドを呼ぶ)
Back 戻るボタン(GoBackメソッド)を呼ぶとき
Forward GoForwardメソッドを呼ぶとき(Windows Phoneでは使わない)
Refresh Refreshメソッドを呼ぶとき(Windows Phoneでは使わない)

ViewModelを保存するのは戻るボタン(あるいはGoBackメソッド)を押した時以外にページを去るときです。たとえばTaskやChooserを起動したりスタートボタンを押す時です。*1戻るボタンを押したときはこのページは破棄されるのでデータを復元する必要がないため保存する処理を行いません。

復元するタイミング

OnNavigatedToでページの状態を復元します。

復元するときはさまざまなことを考慮しなければなりません。

  • 新規のページとして表示するのか?それとも以前の状態を復元するのか?
  • このページは新しく生成されたのか?それとも以前のページなのか?


これらのことを考慮しなければならないため処理が複雑になりがちです。しかし考慮すべきことは決まっているのでOnNavigatedToで行う処理は下記のようにパターン化することができます。

bool _isNewPageInstance = false;

public コンストラクタ()
{
    InitializeComponent();

    _isNewPageInstance = true; // 新たにページが生成されるときはtrueに設定する
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    if (_isNewPageInstance)
    {
        // _viewModelがnullのときは、新規でデータを作るとき
        // _viewModelがnullでない場合は、データを編集するページのように予めこのページで表示する値が設定されているとき
        if (_viewModel == null)
        {
            if (State.Count > 0)
            {
                // ViewModelの復元
                _viewModel = State["ViewModel"] as ViewModel;
            }
            else
            {
                // ViewModeの初期化
                _viewModel = new ViewModel();
            }
        }
        DataContext = _viewModel;
    }

    // Stateの削除
    if (State.ContainsKey("ViewModel"))
        State.Remove("ViewModel");

    _isNewPageInstance = false; // 元に戻す

    base.OnNavigatedTo(e);
}
_isNewPageInstance変数について

_isNewPageInstance変数はページが新たに生成されたかどうかを表す変数です。メモリ上にページが残っている場合はこのフラグはfalseになり、新たにページが作られるときはこのフラグがtrueになります。
■新たにページが作成されるとき

  • Navigateされたとき
  • Tombstonedのとき

これらのときには_isNewPageInstance変数にtrueがコンストラクタで設定されます。
■ページが生成されないとき

  • アプリケーション内で戻るボタンを押した時
  • DatePickerやListPickerなどのコントロールから戻ってきた時
  • TaskやChooserから戻ってきた時
  • Dormantのとき

これらのときはメモリ上にページが存在するので_isNewPageInstance変数にfalseが設定されます。

_isNewPageInstance変数がtrueのときは初期化あるいは復元の処理が必要であり、_isNewPageInstanceがfalseのときはなにもする必要がありません。


このようにWindows Phone上ではさまざまな状態を考慮する必要があります。


参考:方法: Windows Phone のページの状態を維持および復元する

*1:TaskやChooserのときはページがメモリ上に残っているのでViewModelを保存する必要はありませんが、その判断が付かないため保存しています