среда, 13 июня 2012 г.

Хранение настроек в Windows 8 приложениях

Сегодня мы поговорим о том, как хранить настройки в Windows 8 приложениях. Причем рассмотрим примеры хранения настроек локально и в сетевом хранилище.


Итак, в Metro Style приложениях вы можете хранить настройки программы как локально на устройстве (например позиционирование элементов под конкретное разрешение), так и в облаке, в этом случае доступ будет со всех устройств пользователя (например можно хранить рекорд пользователя).
В рамках первого примера, рассмотрим сохранение одиночных значений в локальном хранилище. Для демонстрации, на странице приложения, я размещу прямоугольник, который можно будет таскать при помощи мыши. Когда приложение перезапускается, положение у прямоугольника должно быть такое, какое оставил пользователь в прошлый раз.
XAML главной страницы будет иметь вид:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Rectangle x:Name="movedRectangle" Width="100" Height="50" Fill="Red" ManipulationMode="All" ManipulationDelta="Rectangle_ManipulationDelta_1" />
Grid>
Соответственно, в коде введем дополнительное поле и обработчик для события ManipulationDelta:

private TranslateTransform dragTranslation;
private void Rectangle_ManipulationDelta_1(object sender, ManipulationDeltaRoutedEventArgs e)
{
    if (dragTranslation == null)
    {
        dragTranslation = new TranslateTransform();
        movedRectangle.RenderTransform = dragTranslation;
    }
    dragTranslation.X += e.Delta.Translation.X;
    dragTranslation.Y += e.Delta.Translation.Y;
}

Теперь, при запуске приложения, в центре экрана будет появляться красный прямоугольник, который мы можем таскать мышкой (кстати, забавный эффект, если схватить, резко дернуть в сторону и отпустить, прямоугольник вылетает за пределы экрана):
Нам, теперь необходимо реализовать загрузку положения прямоугольника при старте приложения и сохранение настроек, при переходе приложения в режим ожидания. Добавим загрузку параметров в конструктор главного окна и подпишемся на событие перехода в режим ожидания:

public MainPage()
{
    this.InitializeComponent();
    // Получаем локальные настройки приложения
    ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
    // Пытаемся прочитать предыдущее положение прямоугольника
    if (localSettings.Values["x"] != null && localSettings.Values["y"] != null)
    {
        dragTranslation = new TranslateTransform();
        movedRectangle.RenderTransform = dragTranslation;           
        dragTranslation.X += double.Parse(localSettings.Values["x"].ToString());
        dragTranslation.Y += double.Parse(localSettings.Values["y"].ToString());
    }

    // Подписываемся на событие перехода приложением в режим ожидания
    Application.Current.Suspending += Current_Suspending;
}
void Current_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
    // Сохраняем настройки
    if (dragTranslation != null)
    {
        ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
        localSettings.Values["x"] = dragTranslation.X;
        localSettings.Values["y"] = dragTranslation.Y;
    }
}
Едиственно, чтобы проверить как это работает, мне пришлось компьютер отправить в спящий режим, т.к. иначе событие Suspending для приложения не вызывалось. Но после включения, прямоугольник остался на том месте в котором я его оставил (причем при запуске из Visual Studio в режиме debug я убедился, что значения считываются правильно).
Если обратили внимание, то я использую вот такую жуткую конструкцию: double.Parse(localSettings.Values["x"].ToString());. На самом деле, в Values хранится тот тип простого параметра который я туда записал. И можно воспользоваться приведением типов, для того чтобы его получить. Но что делать, если мне надо хранить не простые значения, а составные? Например, не просто x и y, а их пару? Смотрим второй пример.
Второй пример, на хранение комплексных типов. Я буду решать туже задачу, только сохранять буду комплексный тип (содержащий x и y), ну и не буду использовать Parse. Для этого, я изменю следующим образом конструктор и методо обработчика события приостановки приложения:

public MainPage()
{
    this.InitializeComponent();
    // Получаем локальные настройки приложения
    ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
    // Пытаемся прочитать предыдущее положение прямоугольника
    if (localSettings.Containers.ContainsKey("point"))
    {
        ApplicationDataContainer point = localSettings.Containers["point"];
        dragTranslation = new TranslateTransform();
        movedRectangle.RenderTransform = dragTranslation;           
        dragTranslation.X += (double)point.Values["x"];
        dragTranslation.Y += (double)point.Values["y"];
    }
    // Подписываемся на событие перехода приложением в режим ожидания
    Application.Current.Suspending += Current_Suspending;
}
void Current_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
    // Сохраняем настройки
    if (dragTranslation != null)
    {
        ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
        localSettings.CreateContainer("point", Windows.Storage.ApplicationDataCreateDisposition.Always);
        localSettings.Containers["point"].Values["x"] = dragTranslation.X;
        localSettings.Containers["point"].Values["y"] = dragTranslation.Y;
    }
}

Кстати, сохранение параметров можно записать и вот так:


void Current_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
    // Сохраняем настройки
    if (dragTranslation != null)
    {
        ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
        ApplicationDataContainer point = localSettings.CreateContainer("point", Windows.Storage.ApplicationDataCreateDisposition.Always);
        point.Values["x"] = dragTranslation.X;
        point.Values["y"] = dragTranslation.Y;
    }
}

Ну и в третьем примере, давайте перейдем к хранению настроек в облаке. Изменения по сравнению с предыдущим примером минимальны. Вместо LocalSettings необходимо использовать RoamingSettings:

public MainPage()
{
    this.InitializeComponent();
    // Получаем глобальные настройки приложения
    ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
    // Пытаемся прочитать предыдущее положение прямоугольника
    if (roamingSettings.Containers.ContainsKey("point"))
    {
        ApplicationDataContainer point = roamingSettings.Containers["point"];
        dragTranslation = new TranslateTransform();
        movedRectangle.RenderTransform = dragTranslation;           
        dragTranslation.X += (double)point.Values["x"];
        dragTranslation.Y += (double)point.Values["y"];
    }
    // Подписываемся на событие перехода приложением в режим ожидания
    Application.Current.Suspending += Current_Suspending;
}
void Current_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
    // Сохраняем настройки
    if (dragTranslation != null)
    {
        ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
        ApplicationDataContainer point = roamingSettings.CreateContainer("point", Windows.Storage.ApplicationDataCreateDisposition.Always);
        point.Values["x"] = dragTranslation.X;
        point.Values["y"] = dragTranslation.Y;
    }
}
Ну и последний четвертый пример, на отслеживание изменения настроек в облаке. Для этого, подпишемся на соответствующее событие и попробуем сохранять данные на каждое изменение и читать, соответственно также на каждое изменение:

public MainPage()
{
    this.InitializeComponent();
    // Подписываемся на изменение данных
    ApplicationData.Current.DataChanged += Current_DataChanged;
    ...
}
void Current_DataChanged(ApplicationData sender, object args)
{
    // Перечитываем настройки
    ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
    // Пытаемся прочитать положение прямоугольника
    if (roamingSettings.Containers.ContainsKey("point") && dragTranslation != null)
    {
        ApplicationDataContainer point = roamingSettings.Containers["point"];
        dragTranslation.X += (double)point.Values["x"];
        dragTranslation.Y += (double)point.Values["y"];
    }
}
private void Rectangle_ManipulationDelta_1(object sender, ManipulationDeltaRoutedEventArgs e)
{
    if (dragTranslation == null)
    {
        dragTranslation = new TranslateTransform();
        movedRectangle.RenderTransform = dragTranslation;
    }
    dragTranslation.X += e.Delta.Translation.X;
    dragTranslation.Y += e.Delta.Translation.Y;
    ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
    ApplicationDataContainer point = roamingSettings.CreateContainer("point", Windows.Storage.ApplicationDataCreateDisposition.Always);
    point.Values["x"] = dragTranslation.X;
    point.Values["y"] = dragTranslation.Y;
}
Теперь, если запустить приложение на двух устройствах с Windows 8, то при перетаскивании прямоугольника на одном, он должен сам перетаскиваться на другом.
P.s. Т.к. у меня нет двух устройств с Windows 8 на борту, то если у кого есть и он попробует последний пример, отпишитесь, очень интересно...

 

2 комментария:

  1. прямоугольник вылетает за пределы экрана << уже заложена кинетическая прокрутка?

    И ещё - объём в облаке ограничивают? Завязка только с серверами MS или можно и свой организовать?

    * ищет шапочку из фольги :D

    ОтветитьУдалить
  2. Да, ограничивают. Сейчас бесплатно 7 ГБ (310 руб/год стоит 20 ГБ, за 1570 руб/год - 100 ГБ). Из приложения квоту можно посмотреть через ApplicationData.RoamingStorageQuota (возвращает в КБ). Других хранилищ кроме MS насколько я знаю подключить нельзя.

    ОтветитьУдалить