Разработка компьютерных игр для Windows Phone 7 с использованием технологий Silverlight и XNA

Работа с данными, сохранение и загрузка параметров игры

Разбить на страницы
Показывать лекцию целиком

Цель работы: Научиться организовывать обмен данными между страницами приложения, научиться обрабатывать события жизненного цикла приложения для сохранения и загрузке данных игры

32.1. Передача данных между страницами

Создадим новый проект P25_1, реализуем в нём следующую функциональность. Он должен иметь две страницы. На стартовой странице пользователю предлагают ввести имя, после чего можно, нажав на кнопку Начать игру, попасть на вторую страницу, которая является игровой. На второй странице нужно использовать введенное ранее имя пользователя для того, чтобы поприветствовать пользователя. Пользователь касается экрана, каждое касание увеличивает счётчик очков. Когда пользователь нажимает на кнопку Назад, осуществляется возврат к главной странице, на которой выводится информация о количестве очков, которые набрал пользователь.

Наш проект будет содержать две страницы. Одну – главную – MainPage.xaml, вторую – игровую – Game.xaml.

На главную страницу добавим элемент управления TextBox, введем в него текст "Введите ваше имя", зададим обработчик события GetFocus. Добавим на страницу новую кнопку, подпишем "Начать игру", создадим обработчик события Click. Кроме того, добавим элемент TextBlock с текстом "Результаты предыдущей игры:" и еще один TextBlock без текста. Будем использовать его для вывода количества очков, набранных пользователем.

Перейдём в файл App.xaml.cs. Здесь мы собираемся создать общедоступное поле, которое будем использовать для передачи данных со страницы Game на страницу MainPage. В код страницы добавим следующее, листинге 32.1.

namespace P25_1
{
    public partial class App : Application
    {

        public int Scores=0;

Данным полем мы можем пользоваться во всех страницах программы. Подобный механизм универсален, то есть, мы вполне могли бы передать значение с главной страницы на игровую, воспользовавшись общим полем в классе App нашего приложения. Однако, для передачи данных на вызываемую страницу предусмотрен специальный механизм, который мы здесь и реализуем.

Вернёмся к странице MainPage, к её cs-файлу. В файле уже есть пара обработчиков событий. Добавим в код перегруженный метод OnNavigatedTo(). Это метод PhoneApplicationPage, он вызывается, когда страница становится активной. В этом методе для страницы MainPage нам нужно попытаться прочитать значение поля Scores из класса App, если в нём есть данные – мы выведем их.

В листинге 32.2 вы можете видеть код вышеупомянутых трех методов, описанных в MainPage.xaml.cs.

 //Нажатие на кнопку
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            //Формируем адрес
            string gamePageCall = "/Game.xaml";
            //Добавляем параметры
            gamePageCall = gamePageCall + "?" + "Username=" + textBox1.Text;
            //Вызываем страницу
            NavigationService.Navigate(new Uri(gamePageCall, UriKind.Relative));
        }
        //Получение фокуса текстовым полем
        private void textBox1_GotFocus(object sender, RoutedEventArgs e)
        {
            textBox1.Text = "";
        }
//Активация страницы
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            int scores = (Application.Current as App).Scores;
            //Сформируем сообщение о набранных очках
            if (textBox1.Text != ""  textBox1.Text != "Введите ваше имя")
            {
                textBlock2.Text = "Пользователь " + textBox1.Text + " набрал " + scores.ToString() + " очка (ов) (о)";
            }
            base.OnNavigatedTo(e);
        }

В обработчике события Click для кнопки мы формируем адрес для вызова страницы Game. К адресу страницы добавляем текст вида "?Username=Имя_пользователя". Если нужно передать несколько параметров, они разделяются знаком "". Сформировав адрес, вызываем страницу Game.

В методе OnNavigateTo мы получаем данные из поля Scores, описанного в классе App, формируем строку для вывода. Причём, обратите внимание, мы выводим количество набранных очков, основываясь на тех данных об очках, которые ранее в поле Scores записала страница Game, а имя пользователя берем из элемента управления TextBox, который расположен на странице MainPage. Это значение автоматически сохраняется при вызове страницы Game. В то же время, если мы уходим со страницы Game, возвращаясь на страницу MainPage, данные Game не сохраняются, каждый новый вызов этой страницы создаёт ее новый экземпляр.

На рис. 32.1 приведено изображение страницы MainPage в процессе разработки.

(рис 32.1) Страница MainPage в процессе разработки

Займемся теперь страницей Game.

Мы добавим на неё четыре элемента управления TextBlock. Будем использовать их для вывода приветствия игроку, для вывода правил игры "Набирайте очки, касаясь экрана", для текста "Очков набрано:" и, собственно, для отображения и хранения количества набранных очков. Это поле установлено в 0, рис. 32.2.

(рис 32.2) Страница Game в процессе разработки

Для подсчёта количества касаний экрана мы используем обработчик события Tap объекта PhoneApplicationPage. Кроме того, нам понадобится перегруженный метод OnNavigatedTo для приёма, разбора и использования параметров, переданных на страницу Game с главной страницы, и обработчик события BackKeyPress объекта PhoneApplicationPage для обработки нажатия на кнопку Назад. В этом обработчике мы будем сохранять количество очков, набранное пользователем при уходе со страницы. Код вышеописанных методов представлен в листинге 32.3.

//Приём, разбор и применение переданных параметров
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            IDictionary<string, string> par = this.NavigationContext.QueryString;

            if (par.ContainsKey("Username"))
            {
                textBlock1.Text="Здравствуйте, "+par["Username"]+"!";
            }
            base.OnNavigatedTo(e);
        }
        //Подсчёт числа касаний экрана
        private void PhoneApplicationPage_Tap(object sender, GestureEventArgs e)
        {
            textBlock4.Text = (Convert.ToInt32(textBlock4.Text) + 1).ToString();
        }
        //Запись количества набранных очков
        private void PhoneApplicationPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            (Application.Current as App).Scores = Convert.ToInt32(textBlock4.Text);
        }

В методе OnNavigatedTo мы принимаем параметры, размещаем их в словарь par, ключи и значения которого являются строками. После этого проверяем, имеется ли в словаре ключ Username, если имеется, используем его значение для вывода приветствия пользователю.

В обработчике события Tap мы преобразуем данные, хранящиеся в поле Text соответствующего текстового блока, в целое число, прибавляем к полученному числу 1, преобразуем то, что получилось, в строку и записываем в поле Text этого же поля. Таким образом, мы используем данное поле и для хранения и для вывода информации об очках.

Перехватив событие нажатия на кнопку Назад, мы обращаемся к полю Scores нашего класса App и помещаем туда приведенное к целочисленному типу значение свойства Text соответствующего текстового поля.

Вышеописанные методы передачи данных между страницами (в особенности тот, который использует хранение данных в классе App), универсальны, их главной особенностью является то, что используя их, мы можем хранить данные лишь до тех пор, пока приложение не закрыто. Когда приложение закрывается и открывается вновь, данные, хранимые в полях класса App, не сохраняются. То есть, например, так не реализовать сохранение состояния игры, которое может храниться после закрытия приложения и которое может быть восстановлено при его повторном запуске. Для постоянного хранения данных можно воспользоваться инструментами, которые предоставляет изолированное хранилище данных.

32.2. Изолированное хранилище, хранение настроек

Мы уже работали с изолированным хранилищем (IsolatedStorage) в практической работе №13, используя для создания игр XNA. Напомним, что в изолированном хранилище мы можем сохранять файлы (мы занимались этим в XNA) и управлять ими. Кроме того, имеется специальная область хранилища (изолированное хранилище настроек), где можно хранить настройки приложения в словаре, содержащем ключи и значения.

В классе App содержатся обработчики событий жизненного цикла приложения – запуска, активации, деактивации и закрытия. Ориентироваться в сохранении и загрузке данных можно на эти события (при запуске и активации восстанавливать данные, при деактивации и закрытии – сохранять), но вполне возможно использовать для сохранения и загрузки какие-то другие события. Например, как это принято в современных играх, можно периодически сохранять состояние игры в процессе игры, можно предоставить пользователю интерфейс для самостоятельного сохранения игры и загрузки сохранённых игр и так далее.

Создадим новый проект, P25_2, на основе предыдущего. Состав страниц проекта и их визуальное оформление не изменилось, а вот код переработан. Он будет иметь следующие особенности. При запуске программа пытается восстановить данные пользователя (имя и количество набранных очков), которые были зафиксированы в предыдущем сеансе работы. При закрытии программы вышеуказанные данные сохраняются. При работе программы мы будем хранить эти данные в полях класса App, эти же поля будем сохранять, и их же будем восстанавливать при запуске приложения.

В листинге 32.4. приведен код класса App, который содержит команды для работы с изолированным хранилищем настроек. Для удобства в листинге (но не в коде класса) мы опустили те автоматически созданные механизмы класса, которые остаются в значении по умолчанию. Обратите внимание, для работы с изолированным хранилищем мы подключили пространство имен System.IO.IsolatedStorage.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO.IsolatedStorage;

namespace P25_2
{
    public partial class App : Application
    {
        public int Scores=0;
        public string Username="";

        // Код для выполнения при запуске приложения (например, из меню "Пуск")
        // Этот код не будет выполняться при повторной активации приложения
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
            LoadSettings();
        }

        // Код для выполнения при активации приложения (переводится в основной режим)
        // Этот код не будет выполняться при первом запуске приложения
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
            LoadSettings();
        }

        // Код для выполнения при деактивации приложения (отправляется в фоновый режим)
        // Этот код не будет выполняться при закрытии приложения
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
            SaveSettings(); 
        }

        // Код для выполнения при закрытии приложения (например, при нажатии пользователем кнопки "Назад")
        // Этот код не будет выполняться при деактивации приложения
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
            SaveSettings(); 
        }
        void LoadSettings()
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
            int scores;
            string username;
            if (settings.TryGetValue<int>("scores", out scores))
            {
                Scores = scores;
            }
            if (settings.TryGetValue<string>("username", out username))
            {
                Username = username;
            }

        } 
        void SaveSettings() 
        { 
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
            settings["scores"] = Scores;
            settings["username"] = Username;
            settings.Save();  
        }

    }
}

У класса имеются два общедоступных поля, одно из них типа int, другое – string.

Мы создаем два метода, один, SaveSettings, используется для сохранения данных в изолированное хранилище настроек. Мы получаем хранилище настроек, после чего записываем в него наши данные с ключами "scores" и "username". Если такие ключи уже присутствуют в хранилище, данные будут перезаписаны, если нет (при первой записи) – они будут созданы. После записи в хранилище мы сохраняем изменения.

Второй метод – LoadSettings, выглядит сложнее. Здесь мы сначала проверяем, имеются ли в хранилище данные с нужными ключами. Если имеются – записываем то, что удалось прочитать, в переменные Scores и Username, соответственно.

Мы вызываем метод SaveSettings при деактивации и закрытии приложения, так как у нас нет гарантии того, что деактивированное приложение выйдет из фонового режима и будет после этого закрыто пользователем, поэтому сохранения данных лишь при закрытии приложения недостаточно.

Мы вызываем метод LoadSettings при запуске приложения или при его активации, то есть, при возвращении из фонового режима.

Посмотрим теперь на код страницы MainPage, для удобства он приведен целиком в листинге 32.5.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace P25_2
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Конструктор
        public MainPage()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {

            if (textBox1.Text == "" || textBox1.Text == "Введите ваше имя")
            {
                MessageBox.Show("Введите имя пользователя!");
            }
            else
            {
                //Сохраняем имя пользователя
                (Application.Current as App).Username = textBox1.Text;
                //Формируем адрес
                string gamePageCall = "/Game.xaml";
                NavigationService.Navigate(new Uri(gamePageCall, UriKind.Relative));
            }
        }

        private void textBox1_GotFocus(object sender, RoutedEventArgs e)
        {
            textBox1.Text = "";
        }
        //Активация страницы
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            int scores = (Application.Current as App).Scores;
            string username = (Application.Current as App).Username;
            if (username != "")
            {
                textBox1.Text = username;
                //Сформируем сообщение о набранных очках, это, как минимум, 0 очков
                textBlock2.Text = "Пользователь " + username + " набрал " + scores.ToString() + " очка (ов) (о)";
            }
            else
            {
                textBox1.Text = "Введите ваше имя";
            }

            base.OnNavigatedTo(e);
        }
    }
}

Здесь мы, в методе OnNavigatedTo (он выполняется и при первом запуске приложения, и при возврате на эту страницу со страницы Game), читаем значения переменных Scores и Username из полей класса App. Если имя пользователя не пустое, мы выводим его в поле textBox1 и выводим информацию о том, сколько очков набрал пользователь за прошлый сеанс игры. Если поле пустое, в textBox1 попадает приглашение ввести имя пользователя.

При щелчке по кнопке мы проверяем, заполнил ли пользователь поле, сравнивая его значение с пустой строкой (если он, например, щёлкнул по полю, но ничего не вводил), либо со строкой "Введите ваше имя". Она отображается в поле в том случае, если программа запущена в первый раз или тогда, когда запуск не первый, но в предыдущем запуске пользователь в поле ничего не вводил. Если поле заполнено, мы записываем его в общедоступную переменную класса App и вызываем страницу Game. Параметры при вызове нам в данном случае не нужны, так как мы используем для обмена данными между страницами открытые поля класса App.

Посмотрим код страницы Game, листинг 32.6.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace P25_2
{
    public partial class Game : PhoneApplicationPage
    {
        public Game()
        {
            InitializeComponent();
        }
        //Получение и применение данных
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            string username = (Application.Current as App).Username;
            textBlock1.Text = "Здравствуйте, " + username+ "!";
            base.OnNavigatedTo(e);
        }
        //Подсчёт числа касаний экрана
        private void PhoneApplicationPage_Tap(object sender, GestureEventArgs e)
        {
            textBlock4.Text = (Convert.ToInt32(textBlock4.Text) + 1).ToString();
        }
        //Запись количества набранных очков
        private void PhoneApplicationPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            (Application.Current as App).Scores = Convert.ToInt32(textBlock4.Text);
        }
    }
}

Главная, новая, особенность здесь заключается в том, что в методе OnNavigateTo мы получаем значение свойства Username из App и используем его при формировании приветствия пользователя.

32.3. Выводы

В этой лабораторной работе мы освоили хранение в приложении данных, доступных всем страницам, передачу параметров при вызове страниц, использование изолированного хранилища настроект.

32.4. Задание

Создайте Silverlight-приложение, которое использует для хранения данных изолированное хранилище и хранит данные в виде XML-файла.

32.5. Дополнительные материалы

К данной лабораторной работе прилагается видеоурок

и демонстрационный проект.

Страницы:

Цель работы: Научиться организовывать обмен данными между страницами приложения, научиться обрабатывать события жизненного цикла приложения для сохранения и загрузке данных игры

32.1. Передача данных между страницами

Создадим новый проект P25_1, реализуем в нём следующую функциональность. Он должен иметь две страницы. На стартовой странице пользователю предлагают ввести имя, после чего можно, нажав на кнопку Начать игру, попасть на вторую страницу, которая является игровой. На второй странице нужно использовать введенное ранее имя пользователя для того, чтобы поприветствовать пользователя. Пользователь касается экрана, каждое касание увеличивает счётчик очков. Когда пользователь нажимает на кнопку Назад, осуществляется возврат к главной странице, на которой выводится информация о количестве очков, которые набрал пользователь.

Наш проект будет содержать две страницы. Одну – главную – MainPage.xaml, вторую – игровую – Game.xaml.

На главную страницу добавим элемент управления TextBox, введем в него текст "Введите ваше имя", зададим обработчик события GetFocus. Добавим на страницу новую кнопку, подпишем "Начать игру", создадим обработчик события Click. Кроме того, добавим элемент TextBlock с текстом "Результаты предыдущей игры:" и еще один TextBlock без текста. Будем использовать его для вывода количества очков, набранных пользователем.

Перейдём в файл App.xaml.cs. Здесь мы собираемся создать общедоступное поле, которое будем использовать для передачи данных со страницы Game на страницу MainPage. В код страницы добавим следующее, листинге 32.1.

namespace P25_1
{
    public partial class App : Application
    {

        public int Scores=0;

Данным полем мы можем пользоваться во всех страницах программы. Подобный механизм универсален, то есть, мы вполне могли бы передать значение с главной страницы на игровую, воспользовавшись общим полем в классе App нашего приложения. Однако, для передачи данных на вызываемую страницу предусмотрен специальный механизм, который мы здесь и реализуем.

Вернёмся к странице MainPage, к её cs-файлу. В файле уже есть пара обработчиков событий. Добавим в код перегруженный метод OnNavigatedTo(). Это метод PhoneApplicationPage, он вызывается, когда страница становится активной. В этом методе для страницы MainPage нам нужно попытаться прочитать значение поля Scores из класса App, если в нём есть данные – мы выведем их.

В листинге 32.2 вы можете видеть код вышеупомянутых трех методов, описанных в MainPage.xaml.cs.

 //Нажатие на кнопку
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            //Формируем адрес
            string gamePageCall = "/Game.xaml";
            //Добавляем параметры
            gamePageCall = gamePageCall + "?" + "Username=" + textBox1.Text;
            //Вызываем страницу
            NavigationService.Navigate(new Uri(gamePageCall, UriKind.Relative));
        }
        //Получение фокуса текстовым полем
        private void textBox1_GotFocus(object sender, RoutedEventArgs e)
        {
            textBox1.Text = "";
        }
//Активация страницы
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            int scores = (Application.Current as App).Scores;
            //Сформируем сообщение о набранных очках
            if (textBox1.Text != ""  textBox1.Text != "Введите ваше имя")
            {
                textBlock2.Text = "Пользователь " + textBox1.Text + " набрал " + scores.ToString() + " очка (ов) (о)";
            }
            base.OnNavigatedTo(e);
        }

В обработчике события Click для кнопки мы формируем адрес для вызова страницы Game. К адресу страницы добавляем текст вида "?Username=Имя_пользователя". Если нужно передать несколько параметров, они разделяются знаком "". Сформировав адрес, вызываем страницу Game.

В методе OnNavigateTo мы получаем данные из поля Scores, описанного в классе App, формируем строку для вывода. Причём, обратите внимание, мы выводим количество набранных очков, основываясь на тех данных об очках, которые ранее в поле Scores записала страница Game, а имя пользователя берем из элемента управления TextBox, который расположен на странице MainPage. Это значение автоматически сохраняется при вызове страницы Game. В то же время, если мы уходим со страницы Game, возвращаясь на страницу MainPage, данные Game не сохраняются, каждый новый вызов этой страницы создаёт ее новый экземпляр.

На рис. 32.1 приведено изображение страницы MainPage в процессе разработки.

(рис 32.1) Страница MainPage в процессе разработки

Займемся теперь страницей Game.

Мы добавим на неё четыре элемента управления TextBlock. Будем использовать их для вывода приветствия игроку, для вывода правил игры "Набирайте очки, касаясь экрана", для текста "Очков набрано:" и, собственно, для отображения и хранения количества набранных очков. Это поле установлено в 0, рис. 32.2.

(рис 32.2) Страница Game в процессе разработки

Для подсчёта количества касаний экрана мы используем обработчик события Tap объекта PhoneApplicationPage. Кроме того, нам понадобится перегруженный метод OnNavigatedTo для приёма, разбора и использования параметров, переданных на страницу Game с главной страницы, и обработчик события BackKeyPress объекта PhoneApplicationPage для обработки нажатия на кнопку Назад. В этом обработчике мы будем сохранять количество очков, набранное пользователем при уходе со страницы. Код вышеописанных методов представлен в листинге 32.3.

//Приём, разбор и применение переданных параметров
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            IDictionary<string, string> par = this.NavigationContext.QueryString;

            if (par.ContainsKey("Username"))
            {
                textBlock1.Text="Здравствуйте, "+par["Username"]+"!";
            }
            base.OnNavigatedTo(e);
        }
        //Подсчёт числа касаний экрана
        private void PhoneApplicationPage_Tap(object sender, GestureEventArgs e)
        {
            textBlock4.Text = (Convert.ToInt32(textBlock4.Text) + 1).ToString();
        }
        //Запись количества набранных очков
        private void PhoneApplicationPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            (Application.Current as App).Scores = Convert.ToInt32(textBlock4.Text);
        }

В методе OnNavigatedTo мы принимаем параметры, размещаем их в словарь par, ключи и значения которого являются строками. После этого проверяем, имеется ли в словаре ключ Username, если имеется, используем его значение для вывода приветствия пользователю.

В обработчике события Tap мы преобразуем данные, хранящиеся в поле Text соответствующего текстового блока, в целое число, прибавляем к полученному числу 1, преобразуем то, что получилось, в строку и записываем в поле Text этого же поля. Таким образом, мы используем данное поле и для хранения и для вывода информации об очках.

Перехватив событие нажатия на кнопку Назад, мы обращаемся к полю Scores нашего класса App и помещаем туда приведенное к целочисленному типу значение свойства Text соответствующего текстового поля.

Вышеописанные методы передачи данных между страницами (в особенности тот, который использует хранение данных в классе App), универсальны, их главной особенностью является то, что используя их, мы можем хранить данные лишь до тех пор, пока приложение не закрыто. Когда приложение закрывается и открывается вновь, данные, хранимые в полях класса App, не сохраняются. То есть, например, так не реализовать сохранение состояния игры, которое может храниться после закрытия приложения и которое может быть восстановлено при его повторном запуске. Для постоянного хранения данных можно воспользоваться инструментами, которые предоставляет изолированное хранилище данных.

32.2. Изолированное хранилище, хранение настроек

Мы уже работали с изолированным хранилищем (IsolatedStorage) в практической работе №13, используя для создания игр XNA. Напомним, что в изолированном хранилище мы можем сохранять файлы (мы занимались этим в XNA) и управлять ими. Кроме того, имеется специальная область хранилища (изолированное хранилище настроек), где можно хранить настройки приложения в словаре, содержащем ключи и значения.

В классе App содержатся обработчики событий жизненного цикла приложения – запуска, активации, деактивации и закрытия. Ориентироваться в сохранении и загрузке данных можно на эти события (при запуске и активации восстанавливать данные, при деактивации и закрытии – сохранять), но вполне возможно использовать для сохранения и загрузки какие-то другие события. Например, как это принято в современных играх, можно периодически сохранять состояние игры в процессе игры, можно предоставить пользователю интерфейс для самостоятельного сохранения игры и загрузки сохранённых игр и так далее.

Создадим новый проект, P25_2, на основе предыдущего. Состав страниц проекта и их визуальное оформление не изменилось, а вот код переработан. Он будет иметь следующие особенности. При запуске программа пытается восстановить данные пользователя (имя и количество набранных очков), которые были зафиксированы в предыдущем сеансе работы. При закрытии программы вышеуказанные данные сохраняются. При работе программы мы будем хранить эти данные в полях класса App, эти же поля будем сохранять, и их же будем восстанавливать при запуске приложения.

В листинге 32.4. приведен код класса App, который содержит команды для работы с изолированным хранилищем настроек. Для удобства в листинге (но не в коде класса) мы опустили те автоматически созданные механизмы класса, которые остаются в значении по умолчанию. Обратите внимание, для работы с изолированным хранилищем мы подключили пространство имен System.IO.IsolatedStorage.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO.IsolatedStorage;

namespace P25_2
{
    public partial class App : Application
    {
        public int Scores=0;
        public string Username="";

        // Код для выполнения при запуске приложения (например, из меню "Пуск")
        // Этот код не будет выполняться при повторной активации приложения
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
            LoadSettings();
        }

        // Код для выполнения при активации приложения (переводится в основной режим)
        // Этот код не будет выполняться при первом запуске приложения
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
            LoadSettings();
        }

        // Код для выполнения при деактивации приложения (отправляется в фоновый режим)
        // Этот код не будет выполняться при закрытии приложения
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
            SaveSettings(); 
        }

        // Код для выполнения при закрытии приложения (например, при нажатии пользователем кнопки "Назад")
        // Этот код не будет выполняться при деактивации приложения
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
            SaveSettings(); 
        }
        void LoadSettings()
        {
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
            int scores;
            string username;
            if (settings.TryGetValue<int>("scores", out scores))
            {
                Scores = scores;
            }
            if (settings.TryGetValue<string>("username", out username))
            {
                Username = username;
            }

        } 
        void SaveSettings() 
        { 
            IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
            settings["scores"] = Scores;
            settings["username"] = Username;
            settings.Save();  
        }

    }
}

У класса имеются два общедоступных поля, одно из них типа int, другое – string.

Мы создаем два метода, один, SaveSettings, используется для сохранения данных в изолированное хранилище настроек. Мы получаем хранилище настроек, после чего записываем в него наши данные с ключами "scores" и "username". Если такие ключи уже присутствуют в хранилище, данные будут перезаписаны, если нет (при первой записи) – они будут созданы. После записи в хранилище мы сохраняем изменения.

Второй метод – LoadSettings, выглядит сложнее. Здесь мы сначала проверяем, имеются ли в хранилище данные с нужными ключами. Если имеются – записываем то, что удалось прочитать, в переменные Scores и Username, соответственно.

Мы вызываем метод SaveSettings при деактивации и закрытии приложения, так как у нас нет гарантии того, что деактивированное приложение выйдет из фонового режима и будет после этого закрыто пользователем, поэтому сохранения данных лишь при закрытии приложения недостаточно.

Мы вызываем метод LoadSettings при запуске приложения или при его активации, то есть, при возвращении из фонового режима.

Посмотрим теперь на код страницы MainPage, для удобства он приведен целиком в листинге 32.5.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace P25_2
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Конструктор
        public MainPage()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {

            if (textBox1.Text == "" || textBox1.Text == "Введите ваше имя")
            {
                MessageBox.Show("Введите имя пользователя!");
            }
            else
            {
                //Сохраняем имя пользователя
                (Application.Current as App).Username = textBox1.Text;
                //Формируем адрес
                string gamePageCall = "/Game.xaml";
                NavigationService.Navigate(new Uri(gamePageCall, UriKind.Relative));
            }
        }

        private void textBox1_GotFocus(object sender, RoutedEventArgs e)
        {
            textBox1.Text = "";
        }
        //Активация страницы
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            int scores = (Application.Current as App).Scores;
            string username = (Application.Current as App).Username;
            if (username != "")
            {
                textBox1.Text = username;
                //Сформируем сообщение о набранных очках, это, как минимум, 0 очков
                textBlock2.Text = "Пользователь " + username + " набрал " + scores.ToString() + " очка (ов) (о)";
            }
            else
            {
                textBox1.Text = "Введите ваше имя";
            }

            base.OnNavigatedTo(e);
        }
    }
}

Здесь мы, в методе OnNavigatedTo (он выполняется и при первом запуске приложения, и при возврате на эту страницу со страницы Game), читаем значения переменных Scores и Username из полей класса App. Если имя пользователя не пустое, мы выводим его в поле textBox1 и выводим информацию о том, сколько очков набрал пользователь за прошлый сеанс игры. Если поле пустое, в textBox1 попадает приглашение ввести имя пользователя.

При щелчке по кнопке мы проверяем, заполнил ли пользователь поле, сравнивая его значение с пустой строкой (если он, например, щёлкнул по полю, но ничего не вводил), либо со строкой "Введите ваше имя". Она отображается в поле в том случае, если программа запущена в первый раз или тогда, когда запуск не первый, но в предыдущем запуске пользователь в поле ничего не вводил. Если поле заполнено, мы записываем его в общедоступную переменную класса App и вызываем страницу Game. Параметры при вызове нам в данном случае не нужны, так как мы используем для обмена данными между страницами открытые поля класса App.

Посмотрим код страницы Game, листинг 32.6.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace P25_2
{
    public partial class Game : PhoneApplicationPage
    {
        public Game()
        {
            InitializeComponent();
        }
        //Получение и применение данных
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            string username = (Application.Current as App).Username;
            textBlock1.Text = "Здравствуйте, " + username+ "!";
            base.OnNavigatedTo(e);
        }
        //Подсчёт числа касаний экрана
        private void PhoneApplicationPage_Tap(object sender, GestureEventArgs e)
        {
            textBlock4.Text = (Convert.ToInt32(textBlock4.Text) + 1).ToString();
        }
        //Запись количества набранных очков
        private void PhoneApplicationPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            (Application.Current as App).Scores = Convert.ToInt32(textBlock4.Text);
        }
    }
}

Главная, новая, особенность здесь заключается в том, что в методе OnNavigateTo мы получаем значение свойства Username из App и используем его при формировании приветствия пользователя.

32.3. Выводы

В этой лабораторной работе мы освоили хранение в приложении данных, доступных всем страницам, передачу параметров при вызове страниц, использование изолированного хранилища настроект.

32.4. Задание

Создайте Silverlight-приложение, которое использует для хранения данных изолированное хранилище и хранит данные в виде XML-файла.

32.5. Дополнительные материалы

К данной лабораторной работе прилагается видеоурок

и демонстрационный проект.

Вернуться к учебному плану