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

2012/02/09

Windows Phone 7.5 では、ユーザーがアプリケーションから移動して離れると、アプリケーションは通常休止状態になります。休止状態では、アプリケーションのコードの実行は停止しますが、アプリケーションはメモリ内でそのまま残ります。アプリケーションが休止し、ユーザーがアプリケーションに戻ってくると、アプリケーションは再開し、アプリケーションの各ページの状態は自動的にユーザーが離れる前の状態に復元されます。

ただし、ユーザーが別の操作に移った後、アプリケーションは廃棄状態にされる可能性があります。この場合、アプリケーションはメモリ内に保持されませんが、アプリケーションに関するいくつかの情報が保存されます。このトピックでの最も重要な点としては、PhoneApplicationPage オブジェクトの State ディクショナリが保存されます。ユーザーが、廃棄状態にされたアプリケーションに戻ってきた場合、アプリケーションは、State ディクショナリに保存されているデータを使用して、ユーザーが離れたときと同じ状態に見えるように、ページの状態を復元する必要があります。この復元は主に、ページ上に存在するすべてのコントロールの値をアプリケーションが廃棄状態にされる前の最後の値に設定することから構成されます。

このトピックでは、データ バインディングを使用して、ページのユーザー インターフェイスの状態を簡単に保持し、復元する方法を示します。廃棄と、アプリケーションでライフサイクルを通じて状態を管理する方法の詳細については、「Windows Phone の実行モデルの概要」を参照してください。

次の手順では、アプリケーションが非アクティブ化され、再アクティブ化されたときに、複数のコントロールの状態を保存し、復元するアプリケーションの作成を示します。

ユーザー インターフェイスの状態を保持するアプリケーションを作成するには

  1. Visual Studio で、新しい Windows Phone アプリケーション プロジェクトを作成します。このテンプレートは、Silverlight for Windows Phone カテゴリにあります。

  2. MainPage.xaml にいくつかの基本コントロールを追加します。この例では、TextBox コントロール、Checkbox コントロール、Slider コントロール、RadioButton コントロールを使用しています。より複雑なコントロールについては、後のセクションで説明します。

    下の XAML コードでは、各コントロールが Binding キーワードを使用するプロパティを持ちます。これらは、アプリケーションのライフサイクル中に、保持され、復元されるコントロールのプロパティです。TextBox コントロールの場合、Text プロパティがバインドされています。Slider コントロールの場合、Value プロパティがバインドされています。RadioButton および CheckBox コントロールの場合、IsChecked プロパティがバインドされています。Binding キーワードの後の語、たとえば "Slider1Value" はコントロールがバインドされているフィールドの名前です。これらのフィールドは、このチュートリアルの次の手順で定義します。バインディング式の最後の部分の "Mode=TwoWay" はバインディングが双方向でなければならないことを示します。つまり、コントロールがバインドされているフィールドが変更された場合は、コントロールが更新されます。コントロールの値が変更された場合は、バインドされているフィールドが更新されます。

    MainPage.xaml ファイルで、"ContentPanel" というの名前の Grid 要素に次の XAML コードを配置します。

    <TextBox Text="{Binding TextBox1Text, Mode=TwoWay}" Height="72" HorizontalAlignment="Left" Margin="20,20,0,0" Name="textBox1" VerticalAlignment="Top" Width="440" />
    <CheckBox IsChecked="{Binding CheckBox1IsChecked, Mode=TwoWay}" Content="CheckBox" Height="71" Name="checkBox1" Margin="20,100,0,0"  VerticalAlignment="Top"/>
    <Slider Value="{Binding Slider1Value, Mode=TwoWay}" Height="84" Name="slider1" Width="440" Margin="20,180,0,0"  VerticalAlignment="Top"/>
    <RadioButton IsChecked="{Binding RadioButton1IsChecked, Mode=TwoWay}" Content="RadioButton 1" Height="71" Name="radioButton1" GroupName="RadioButtonGroup"  Margin="20,260,0,0" VerticalAlignment="Top"/>
    <RadioButton IsChecked="{Binding RadioButton1IsChecked, Mode=TwoWay}" Content="RadioButton 2" Height="71" Name="radioButton2" GroupName="RadioButtonGroup"  Margin="20,340,0,0" VerticalAlignment="Top"/>
    
    
  3. 次に、XAML で定義されているコントロールがバインドされているフィールドを含むクラスを追加します。Visual Studio で、[プロジェクト] メニューの [クラスの追加] をクリックします。追加する項目のリストで [クラス] を選択し、[名前] フィールドに「ViewModel.cs」と入力します。[追加] をクリックして、プロジェクトにクラスを追加します。

  4. ファイルの先頭に、シリアル化をサポートする Serialization 名前空間とデータ バインドをサポートする ComponentModel 名前空間の using ディレクティブを追加します。

    using System.Runtime.Serialization;
    using System.ComponentModel;
    
    
  5. ViewModel.cs ファイルで、クラス定義の前に DataContract 属性を追加します。この属性は、このクラスが DataContractSerializer によって、シリアル化と逆シリアル化の操作で使われることを示します。PhoneApplicationPageState プロパティに保存されているすべてのデータは、このクラスと同様に、データ コントラクト シリアル化可能である必要があります。この種類のシリアル化の詳細については、「データ コントラクトの使用」を参照してください。

    クラスで INotifyPropertyChanged インターフェイスを実装することを宣言する必要もあります。このインターフェイスは、バインドされているデータが変更された場合に、ユーザー インターフェイス コントロールの値を自動的に変更できるようにするインターフェイスです。

    [DataContract]
    public class ViewModel : INotifyPropertyChanged
    {
    }
    
    
  6. バインドされている各コントロール プロパティのクラスに、プライベート メンバー変数を追加します。

    private string _textBox1Text;
    private bool _checkBox1IsChecked;
    private bool _radioButton1IsChecked;
    private bool _radioButton2IsChecked;
    private double _slider1Value;
    
    
  7. 次に、INotifyPropertyChanged 機能をクラスに追加します。これには、PropertyChanged イベントの追加と、PropertyChanged イベントを発生させる NotifyPropertyChanged というメソッドの実装が含まれます。

    public event PropertyChangedEventHandler PropertyChanged;
    
    private void NotifyPropertyChanged(string propertyName)
    {
      if (null != PropertyChanged)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    
    
  8. 次に、バインドされた各コントロール プロパティにパブリック プロパティを追加します。これらのプロパティ名は、XAML ファイルの Binding 式で使われている名前です。各プロパティについて、get アクセサーは、単に対応するプライベート メンバー変数の値を返します。put アクセサーはメンバー変数の値を設定し、NotifyPropertyChanged を呼び出して、事実上対応するコントロールに、その値を更新する必要があることを通知します。各プロパティも DataMember 属性でラベル付けされ、シリアライザーに、プロパティをシリアル化する必要があることを知らせます。

    ViewModel クラス定義内に次のコードを貼り付けます。

    [DataMember]
    public string TextBox1Text
    {
      get { return _textBox1Text; }
      set
      {
        _textBox1Text = value;
        NotifyPropertyChanged("TextBox1Text");
      }
    }
    
    [DataMember]
    public bool CheckBox1IsChecked
    {
      get { return _checkBox1IsChecked; }
      set
      {
        _checkBox1IsChecked = value;
        NotifyPropertyChanged("CheckBox1IsChecked");
      }
    }
    
    [DataMember]
    public double Slider1Value
    {
      get { return _slider1Value; }
      set
      {
        _slider1Value = value;
        NotifyPropertyChanged("Slider1Value");
      }
    }
    
    [DataMember]
    public bool RadioButton1IsChecked
    {
      get { return _radioButton1IsChecked; }
      set
      {
        _radioButton1IsChecked = value;
        NotifyPropertyChanged("RadioButton1IsChecked");
      }
    }
    
    [DataMember]
    public bool RadioButton2IsChecked
    {
      get { return _radioButton2IsChecked; }
      set
      {
        _radioButton2IsChecked = value;
        NotifyPropertyChanged("RadioButton2IsChecked");
      }
    }
    
    
  9. UI 状態を保持するための最後の手順は、分離コード ページでページの状態の変更を処理することです。MainPage.xaml.cs の MainPage クラス定義に次のメンバー変数を追加します。最初の変数の型は ViewModel です。これは、前の手順で作成したクラスであり、UI コントロールの値を格納します。2 つ目の変数は、_isNewPageInstance という bool です。この変数は後で、アプリケーションが最初に起動されるか、廃棄後に再起動された場合などに、ページが新しいインスタンスであるかどうかを判断したり、ユーザーがアプリケーション内からページを戻る場合などに、ページが既存のインスタンスであるかどうかを判断するために使用します。

    public partial class MainPage : PhoneApplicationPage
    {
      ViewModel _viewModel;
      bool _isNewPageInstance = false;
    
    
  10. ページ コンストラクターで、_isNewPageInstancetrue に設定します。

    // Constructor
    public MainPage()
    {
      InitializeComponent();
    
      _isNewPageInstance = true;      
    }
    
    
  11. 次に、Page 基本クラスから継承した OnNavigatedFrom(NavigationEventArgs) メソッドをオーバーライドします。このメソッドは、ユーザーがこのページから移動して離れたときに常に呼び出されます。このメソッドでは、UI コントロールの値を格納する ViewModel オブジェクトがページの State ディクショナリに保存されます。アプリケーションが廃棄されても State ディクショナリは保持されるため、アプリケーションが再アクティブ化されたときに、状態を復元するために使用できます。戻る移動の場合、移動が完了したら、ページが破棄され、状態を保存する必要がなくなります。次のメソッド定義を MainPage クラス定義にコピーします。

    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
    {
      // If this is a back navigation, the page will be discarded, so there
      // is no need to save state.
      if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
      {
        // Save the ViewModel variable in the page's State dictionary.
        State["ViewModel"] = _viewModel;
      }
    }
    
    
  12. 最後に、OnNavigatedTo(NavigationEventArgs) メソッドをオーバーライドします。このメソッドは、ユーザーがこのページに移動したときに常に呼び出されます。これには、アプリケーションが初めて起動され、ページが初めて表示される場合、アプリケーションが廃棄され、ユーザーがアプリケーションに戻った場合、ユーザーが単一のインスタンス内でページ間を移動する場合があります。

    まず、_isNewPageInstancetrue であるかどうかを確認します。true でない場合は、ページのインスタンスが既にメモリに存在していることがわかります。これは、UI の状態が変更されておらず、コントロールの値を復元する必要がないことを意味します。これが新しいページ インスタンスの場合は、UI 値を格納する ViewModel オブジェクトがページの State ディクショナリに保存されているかどうかを確認します。格納されている場合は、保存されたオブジェクトを _viewModel メンバー変数に割り当て、格納されていない場合は、保存された状態がないため、新しい ViewModel インスタンスを作成する必要があります。ページの DataContextViewModel オブジェクトに設定します。これにより、すべての UI コントロールが更新され、オブジェクトに保存された値が表示されます。最後に、このメソッドは _isNewPageInstance 変数を false に設定し、まだこのページがメモリにある間に、ユーザーがページに戻ってきた場合、UI は OnNavigatedTo で復元されません。

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
      // If _isNewPageInstance is true, the page constuctor has been called, so
      // state may need to be restored.
      if (_isNewPageInstance)
      {
        if (_viewModel == null)
        {
          if (State.Count > 0)
          {
            _viewModel = (ViewModel)State["ViewModel"];
          }
          else
          {
            _viewModel = new ViewModel();
          }
        }
        DataContext = _viewModel;
      }
      // Set _isNewPageInstance to false. If the user navigates back to this page
      // and it has remained in memory, this value will continue to be false.
      _isNewPageInstance = false;
    }
    
    

Windows Phone 7.5 では、フォアグラウンド アプリケーションを円滑に実行するために十分なメモリがある限り、アプリケーションはユーザーが別の操作に移った時点で休止状態にされます。アプリケーションが休止状態になった後に復元されたとき、UI の状態は自動的に保持されています。廃棄状態にされた後にページの状態が適切に復元されたことを確認するには、デバッガーで自動廃棄を有効にする必要があります。

[プロジェクト] メニューの [[アプリケーション名] のプロパティ] をクリックするか、ソリューション エクスプローラーでプロジェクトを右クリックし、[プロパティ] をクリックして、[プロジェクトのプロパティ] ダイアログを開きます。[デバッグ] タブで、[デバッグ時に非アクティブ化されたら廃棄状態にする] というラベルが付いたチェック ボックスをオンにします。このチェック ボックスをオンにして、アプリケーションのデバッグを開始し、[スタート] ボタンをクリックして、アプリケーションを直ちに廃棄状態にしてから、[戻る] ボタンを押して、アプリケーションに戻り、ページの状態が保存されていて、正しく復元されたことを確認します。

注注:

XNA フレームワーク アプリケーションの場合、[デバッグ時に非アクティブ化されたら廃棄状態にする] チェック ボックスは、[プロジェクトのプロパティ] ダイアログの [XNA Game Studio] タブにあります。

表示: