В предыдущей теме мы сохраняли и обратно загружали в программу текст для текстового поля с помощью объекта Windows.Storage.ApplicationData. Такие данные представляют данные приложения. Но кроме них в приложении имеются еще и данные сеанса.
Данные сеанса являются временными и относятся к текущему сеансу. Если пользователь закрывает приложения, нажав на крестик, то он тем самым завершает сеанс. При этом данные приложения сохраняются, и могут быть восстановлены. А вот данные сеанса будут уже другие. Такое поведение нежелательно в некоторых случаях.
Итак, возьмем проект приложения LifecycleApp из прошлой темы. В нем уже есть страница Page1. Изменим файл Page1.xaml следующим образом:
<Page x:Class="LifecycleApp.Page1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:LifecycleApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid x:Name="LayoutRoot"> <StackPanel> <TextBlock Text="Страница 1" Style="{ThemeResource HeaderTextBlockStyle}" /> <Button Content="На страницу 2" Click="Button_Click" /> <TextBox x:Name="inputBox" /> </StackPanel> </Grid> </Page>
Здесь опять же текстовое поле, а также кнопка для перехода на другую страницу. В итоге получится следующий дизайн:
И также добавим в файл Page1.xaml.cs обработчик нажатия кнопки, по которому будет идти переход на вторую страницу:
private void Button_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(Page2)); }
Теперь добавим саму вторую страницу. Это будет добавление нового элемента по типу Basic Page, и назовем его Page2
В файле Page2.xaml определим следующую разметку:
<Page x:Class="LifecycleApp.Page2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:LifecycleApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid x:Name="LayoutRoot"> <StackPanel> <TextBlock Text="Страница 2" Style="{ThemeResource HeaderTextBlockStyle}" /> <Button Content="На страницу 1" Click="Button_Click" /> </StackPanel> </Grid> </Page>
А в файле кода Page2.xaml.cs определим обработчик кнопки, который бы переходил обратно на первую страницу:
private void Button_Click(object sender, RoutedEventArgs e) { Frame.GoBack(); }
Теперь если мы запустим приложение, перейдем с первой страницы на вторую. И если мы сделаем Suspend and shutdouwn, то приложение вновь запустится с первой страницы. То есть данные о навигации не сохраняются. Подобным образом если ввести что-то в текстовое поле и перезагрузить приложение, то данные опять не сохраняться.
Чтобы сохранить, а затем восстановить данные сеанса, нам потребуется класс SuspensionManager, который добавляется в проект в папку Common вместе с добавлением первого элемента Basic Page.
Вся основная работа по сохранению данных проводится в коде класса App, поэтому откроем файл App.xaml.cs. Поскольку работа приложения
начинается с метода OnLaunched
, то в нем нам надо определить загрузку данных сеанса. И для этого изменим этот метод следующим образом:
protected async override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; // убедимся, что объект Window уже имеет содержимое if (rootFrame == null) { // создаем объект Frame для выполнения навигации rootFrame = new Frame(); // регистрация фрейма LifecycleApp.Common.SuspensionManager.RegisterFrame(rootFrame, "appFrame"); rootFrame.CacheSize = 1; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { // если приложение было завершено операционной системой, восстанавливаем его состояние await LifecycleApp.Common.SuspensionManager.RestoreAsync(); } // помещаем фрейм в текущий объект Window Window.Current.Content = rootFrame; } if (rootFrame.Content == null) { if (rootFrame.ContentTransitions != null) { this.transitions = new TransitionCollection(); foreach (var c in rootFrame.ContentTransitions) { this.transitions.Add(c); } } rootFrame.ContentTransitions = null; rootFrame.Navigated += this.RootFrame_FirstNavigated; if (!rootFrame.Navigate(typeof(Page1), e.Arguments)) { throw new Exception("Failed to create initial page"); } } // убедимся, что текущий объект Window является активным Window.Current.Activate(); }
Ключевым компонентом для отображения приложения на экране является объект Window, который определяется с помощью свойства Window.Current
.
Для загрузки отдельных страниц в окно Window нам нужен объект Frame, который позволяет переключаться между страницами, хранит историю навигации.
Поэтому нам надо проверить, а определен ли уже такой объект Frame, и если неопределен - то создать его и добавить в Window. Для получения объекта Frame
используется выражение Frame rootFrame = Window.Current.Content as Frame
.
При создании объекта Frame нам надо зарегистрировать его: LifecycleApp.Common.SuspensionManager.RegisterFrame(rootFrame, "appFrame")
.
Тем самым мы указываем классу SuspensionManager какой именно фрейм будет в случае необходимости будет приостановлен, если приложение перейдет в состояние Suspended.
И если приложение было приостановлено, а затем завершено операционной системой, то мы восстанавливаем его состояние:
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { await LifecycleApp.Common.SuspensionManager.RestoreAsync(); }
В самом конце метода окно активируется, если оно не является активным: Window.Current.Activate()
Теперь изменим метод OnSuspending
, срабатывающий при приостановке работы приложения. В нем мы будем сохранять данные:
private async void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); await LifecycleApp.Common.SuspensionManager.SaveAsync(); deferral.Complete(); }
В отличие от кода по умолчанию здесь добавлена одна строка для асинхронной загрузки данных: LifecycleApp.Common.SuspensionManager.SaveAsync()
Этого хватит, чтобы сохранить и восстановить данные сеанса, например, историю навигации. И если мы перейдем с первой страницы на вторую и сделаем Suspend and shutdown, то при перезапуске окажемся опять же на второй странице.
Но это еще не все. У нас на первой странице Page1 есть текстовое поле. В прошлой теме его данные сохранялись как данные приложения через
Windows.Storage.ApplicationData
. Сейчас же сохраним его данные как данные сеанса. В файле кода Page1.xaml.cs у нас уже определены методы
NavigationHelper_LoadState
и NavigationHelper_SaveState
, которые призваны загружать и сохранять соответственно данные сеанса для элементов правления.
Изменим их следующим образом:
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) { if (e.PageState != null && e.PageState.ContainsKey("value")) inputBox.Text = e.PageState["value"].ToString(); } private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e) { e.PageState["value"] = inputBox.Text; }