Windows Phone Tutorial


The Windows Phone life cycle is not terribly complicated, but it can be the source of some LifeCycle  confusion, and managing the life-cycle can be intimidating to new Windows Phone programmers.  This posting will review the basic life-cycle of a Windows Phone application and will show you what you need to do to preserve state and give your users a rewarding phone experience.

Reviewing the Life Cycle

When your application is launched (e.g., from the start menu) the Application Launching event is fired. You can hook this event in App.xaml.cs in the Application_Launching event handler, as described in yesterday’s posting on Fast Application Switching.

Once the application is started, and every time the user navigates to your page, you  will receive the OnNavigatedTo method, after which your page will be in the Running state.

If the user starts a new application your application receives the Application Deactivated event and is put in the Dormant state.  If the phone runs low on memory, your application may be Tombstoned.

From either Tombstoned or Dormant your application may be terminated or it may be restored.  What we care about right now is what happens when your application is restored.

If your application is Dormant, you not only don’t have to take any action when you are restored, you don’t want to take any action; all your state was preserved when you were dormant, and you are ready to go.

If on the other hand, your application was tombstoned, then you do want to restore your page state when you return so that it appears to the user that your application was running (or at least dormant) while it was switched away.

Managing Page State

The best way to see how to manage Page State is to build a small application.  Open Visual Studio and create a new Windows Phone application and on the main page add a prompt and a TextBox for the user’s name, and a CheckBox for whether the user is a Geek, as shown in the following code,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<Grid
    x:Name="ContentPanel"
    Grid.Row="1"
    Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition
            Height="1*" />
        <RowDefinition
            Height="1*" />
        <RowDefinition
            Height="4*" />
    </Grid.RowDefinitions>
    <StackPanel
        Grid.Row="0"
        VerticalAlignment="Stretch"
        HorizontalAlignment="Stretch"
        Orientation="Horizontal">
        <TextBlock
            Text="Full Name:"
            Margin="5"
            HorizontalAlignment="Right"
            VerticalAlignment="Center" />
        <TextBox Name="FullName" Text="" Margin="5"
                 Width="300"
            HorizontalAlignment="Right"
            VerticalAlignment="Center" />
    </StackPanel>
    <CheckBox
        Name="Geek"
        Content="Geek?"
        IsChecked="True"
        Grid.Row="1"
        VerticalAlignment="Center" />
 
</Grid>

Binding the Controls

Right now the CheckBox and TextBox have hardcoded values. We’d prefer to bind these values to an object.   Create MainPageViewModel.cs and have it implement INotifyPropertyChanged,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MainPageViewModel : INotifyPropertyChanged
{
 
    private string _userName;
    public string UserName
    {
        get { return _userName; }
        set
        {
            _userName = value;
            NotifyPropertyChanged( "UserName" );
        }
    }
 
    private bool _isGeek;
    public bool IsGeek
    {
        get { return _isGeek; }
        set
        {
            _isGeek = value;
            NotifyPropertyChanged( "IsGeek" );
        }
 
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged( string propName )
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(
                this,
                new PropertyChangedEventArgs( propName ) );
        }
    }
}

Return to the Xaml and change the text property of the TextBox and the content property of the  CheckBox to databind to your new ViewModel,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<TextBox
    Name="FullName"
    Text="{Binding UserName, Mode=TwoWay}"
    Margin="5"
    Width="300"
    HorizontalAlignment="Right"
    VerticalAlignment="Center" />
 
  <CheckBox
     Name="Geek"
     Content="{Binding IsGeek, Mode=TwoWay}"
     IsChecked="True"
     Grid.Row="1"
     VerticalAlignment="Center" />

Add attributes to make the class DataContract Serializable

So far this is straight data-binding, nothing special.  Because you’ll want to save an instance of the MainPageViewModel in Page State, however, you must make it DataContract serializable. You do that with attributes.

Add a reference to the System.Runtime.Serialization library.

Return to the MainPageViewModel.cs file and add the [DataContract] attribute to the class and add [DataMember] attributes to all the properties that you want serialized,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
[DataContract]
public class MainPageViewModel : INotifyPropertyChanged
{
 
    private string _userName;
 
    [DataMember]
    public string UserName
    {
       //...
    }
 
    private bool _isGeek;
 
    [DataMember]
    public bool IsGeek
    {
       //...
    }

Track the state

The final step is to add a flag to the MainPage class to track whether you are seeing a new instance of the page or a restored instance. It is this flag (_isNewPageInstance) that will ensure that your page is efficient. If you return to the page after being tombstoned you want this flag to be true, but if you return after being dormant you want it to be false.

1
2
MainPageViewModel _vm;
bool _isNewPageInstance = false;

[ Note that we also added a member variable to hold the MainPageViewModel instance. ]

The Page Logic

Begin by initializing the flag to false (as shown) but setting the flag to true in your constructor.

1
2
3
4
5
public MainPage()
{
    InitializeComponent();
    _isNewPageInstance = true;
}

Override the OnNavigatedFrom method and save the page state unless the navigation mode is Back, in which case the page is begin discarded and there is no reason to save state,

1
2
3
4
5
6
7
8
9
protected override void OnNavigatedFrom(
    System.Windows.Navigation.NavigationEventArgs e )
{
    if (e.NavigationMode !=
          System.Windows.Navigation.NavigationMode.Back)
    {
        State["MainPageViewModel"] = _vm;
    }
}

Next, override the OnNavigatedTo method. Here is where that flag comes into play.  You might be coming to this page under a number of different conditions. Let’s consider three primary cases:

  • This is your first time on the page – in that case the constructor will be called, and the flag will be true, but there will be nothing in the State dictionary
  • You are returning to this page from being tombstoned – in this case the flag will be true.  When you return from tombstoning the constructor is called and the flag is set to true.
  • You are returning from the application being Dormant – in this case state was preserved and so the flag will be false.

The reason the flag is false when you return from the Dormant state is that we set it to false at the end of OnNavigatedTo (as you’ll see in a moment) and that state is preserved when the page is Dormant. Returning from Dormant does not call the constructor.

The critical difference is that after tombstoning the flag is true (indicating you should go get the state you saved in the Page State dictionary) and after being dormant the flag is false (indicating that you do not need to restore state).

Here’s the override of OnNavigatedTo,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
protected override void OnNavigatedTo(
    System.Windows.Navigation.NavigationEventArgs e )
{
    if (_isNewPageInstance)
    {
        if (_vm == null)
        {
            if (State.ContainsKey( "MainPageViewModel"))
            {
                MessageBox.Show( "Retrieving state..." );
                _vm = State["MainPageViewModel"]
                                   as MainPageViewModel;
            }
            else
            {
                _vm = new MainPageViewModel();
            }
        }
        DataContext = _vm;
    }
    _isNewPageInstance = false;
}

The State Machine

The first time you get here (e.g., when the application is launched) the first if statement evaluates to true (_isNewPageInstance was set to true in the constructor).  The instance of MainPageViewModel will be null but the State object will not contain anything and so you’ll create a new instance of MainPageViewModel.

If you get here from a dormant state then the first if statement will fail ( _isNewPageInstance == false) and you’ll fall through changing nothing and not taking any time to restore state, as state is already intact.  This is a very good thing; no time is wasted restoring state.

Finally, if you get here from being tombstoned the first if statement will evaluate true (remember that you go through the constructor) and the vm will be null, but this time the Page State object will have the MainPageViewModel object and so you can retrieve it.

Run the program and note that the message box does not come up.  Next, stop the application and right click on the project, and choose Properties.  Select the Debug tab and check the checkbox that says Tombstone upon deactivation while debugging.  Run the application and note that the message box does indicate that this time state is being restored.


posted on 2013-03-07 10:13  ConfuciusPei  阅读(149)  评论(0编辑  收藏  举报