Windows 8为应用程序引入了一个新的生命周期,完全不同于传统的桌面应用程序的生命周期。在Windows 8.1中有些变化,在Windows 10中又有一些变化。如果使用Windows 10和平板电脑模式,应用程序的生命周期与桌面模式是不同的。在平板电脑模式中,应用程序通常全屏显示。分离键盘(对于平板电脑设备,如Microsoft Surface),或在Action Center中使用Tablet Mode按钮,可以自动切换到平板模式。在平板模式下运行应用程序时,如果应用程序进入后台(用户切换到另一个应用程序),就会暂停,它不会得到任何更多的CPU利用率。这样,应用程序不消耗任何电力。应用程序在后台时,只使用内存,一旦用户切换到这个应用程序,应用程序就再次激活。
当内存资源短缺时,Windows可以终止暂停应用程序的进程,从而终止该应用程序。应用程序不会收到任何消息,所以不能对此事件做出反应。因此,应用程序应该在进入暂停模式前做一些处理工作,保存其状态。等到应用程序终止时进行处理就晚了。
当收到暂停事件时,应用程序应该将其状态存储在磁盘上。如果再次启动应用程序,应用程序可以显示给用户,好像它从未终止。只需要把页面堆栈的信息存储到用户退出的页面上,恢复页面堆栈,并把字段初始化为用户输入的数据,就允许用户返回。
本节的示例应用程序ApplicationLifeline就完成这个任务。在这个程序中,允许在多个页面之间的导航,可以输入状态。应用程序暂停时,存储页面堆栈和状态,在启动应用程序时恢复它们。
1. 应用程序的执行状态
应用程序的状态使用ApplicationExecutionState枚举定义。该枚举定义了NotRunning、Running、Suspended、Terminated和CloseByUser状态。应用程序需要知道并存储自己的状态,因为用户在返回应用程序时希望继续原来的操作。
在App类的OnLaunched方法中,可以使用LauchActivatedEventArgs参数的PreviousExecutionState属性获取应用程序的前一个执行状态。如果应用程序是在安装后第一次启动,在重启计算机后启动,或者用户上一次在任务管理器中终止了其进程,那么该应用程序的前一个状态是NotRunning。如果用户单击应用程序的图标时应用程序已经激活,或者应用程序通过某个激活协定激活,则其前一个执行状态为Running。如果应用程序被暂停,那么激活它时PreviousExecutionState属性会返回Suspended。一般来说,在这种情况下不需要执行什么特殊操作。因为状态仍然在内存中可用。在暂停状态下,应用程序不使用CPU循环,也没有磁盘访问。
注意:
应用程序可以实现一个或多个协定,然后用其中一个协定激活应用程序。这类协定的一个例子是共享,使用这个协定,用户可以共享另一个应用程序的一些数据,并使用它作为共享目标,启动一个Windows应用程序。实现共享协定参见本章的"共享数据"一节。
2. 在页面之间导航
展示Windows应用程序的生命周期的示例应用程序(ApplicationLifetime)从Blank App模板开始。创建项目后,添加页面Page 1和Page 2,实现页面之间的导航。
在MainPage中,添加两个按钮控件来导航Page 2和Page 1,再添加两个文本框控件,在导航时传递数据:
<StackPanel Spacing="5">
<Button Content="Page 1" Click="{x:Bind GoToPage1,Mode=OneTime}"/>
<TextBox Header="Parameter 1" Text="{x:Bind ParameterPage1,Mode=TwoWay}"/>
<Button Content="Page 2" Click="{x:Bind GoToPage2,Mode=OneTime}"/>
<TextBox Header="Parameter 2" Text="{x:Bind ParameterPage2,Mode=TwoWay}"/>
</StackPanel>
代码隐藏文件包含事件处理程序、Page 1和Page 2的导航代码,以及参数的属性:
public string ParameterPage1 { get; set; }
public string ParameterPage2 { get; set; }
public void GoToPage1() => Frame.Navigate(typeof(Page1), ParameterPage1);
public void GoToPage2() => Frame.Navigate(typeof(Page2), ParameterPage2);
Page 1的UI元素显示在导航到这个页面时接收到的数据、允许用户导航到Page 2的按钮,以及允许用户输入一些状态信息的一个文本框,应用程序终止时会保存这些状态信息:
<StackPanel Spacing="5">
<TextBlock Text="Page 1" Style="{StaticResource HeaderTextBlockStyle}"/>
<TextBlock Text="{x:Bind ReceivedContent,Mode=OneTime}" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBox Text="{x:Bind Parameter,Mode=TwoWay}"/>
<Button Content="Navigate to Page 2" Click="{x:Bind GoToPage2,Mode=OneTime}"/>
<!--<TextBox Header="Session State 1" Text="{x:Bind Data.Session1,Mode=TwoWay}"/>
<TextBox Header="Session State 2" Text="{x:Bind Data.Session2,Mode=TwoWay}"/>-->
</StackPanel>
类似于MainPage ,Page 1的导航代码为导航时传递的数据定义了一个自动实现的属性,和实现导航到Page 2的一个事件处理程序:
public void GoToPage2() => Frame.Navigate(typeof(Page2), Parameter);
public string Parameter { get; set; }
在代码隐藏文件中,导航参数在OnNavigatedTo()方法的重写版本中接收。接收到的参数分配给自动实现的属性ReceivedContent:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ReceivedContent = e.Parameter?.ToString();
}
public string ReceivedContent { get; set; }
在导航的实现代码中,Page 2非常类似于Page 1,所以这里不重复它的实现。
这里,标题栏后退按钮的可见性和处理程序在类BackButtonManager中定义。如果当前Frame的CanGoBack属性返回true,那么构造函数的实现代码使后退按钮可见。如果堆栈可用,就实现OnBackRequested方法,返回页面堆栈:
public class BackButtonManager : IDisposable
{
private SystemNavigationManager _navigationManager;
private Frame _frame;
public BackButtonManager(Frame frame)
{
_frame = frame ?? throw new ArgumentNullException(nameof(frame));
_navigationManager = SystemNavigationManager.GetForCurrentView();
_navigationManager.AppViewBackButtonVisibility = _frame.CanGoBack ?
AppViewBackButtonVisibility.Visible :
AppViewBackButtonVisibility.Collapsed;
_navigationManager.BackRequested += OnBackRequested;
}
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
if (_frame.CanGoBack)
{
_frame.GoBack();
e.Handled = true;
}
}
public void Dispose()
{
_navigationManager.BackRequested -= OnBackRequested;
}
}
在所有的页面中,通过在OnNavigatedTo方法中传递Frame来实例化BackButtonManager,它在OnNavigatedFrom方法中销毁:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_backButtonManager = new BackButtonManager(Frame);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
_backButtonManager.Dispose();
}
有了所有这些代码,用户可以在3个不同的页面之间后退和前进。下一步需要记住页面和页面堆栈,把应用程序导航到用户最近一次访问的页面上。