вторник, 5 июня 2012 г.

Навигация в Windows 8 приложениях

Если не надоест, время от времени буду писать про всякие особенности разработки Windows 8 приложений. И так как любое приложение имеет более одной формы/окна/страницы, то сегодня я приведу несколько примеров того, как можно реализовать навигацию между страницами.
Начнем с того, что создадим приложение Metro Style. Для этого необходимо в Windows 8 запустить Visual Studio 2012 и выбрать необходимый тип проекта:


В свежесозданном приложении будет целая одна страница - MainPage.xaml (нет будет еще App.xaml, манифест и т.д., но страница одна).
Для простоты демонстрации примеров, на главной странице, я заменю Grid на StackPanel и для каждого примера буду кидать в него еще один StackPanel с горизонтальной ориентацией.
Для первого примера простой навигации нам понадобиться только кнопка, для того, чтобы перейти на страницу с информацией о приложении (т.е. просто переход на другую страницу, с возвратом на исходную).
Итак StackPanel будет иметь вот такой вид:

<StackPanel Orientation="Horizontal">
    <TextBlock Text="Пример простого перехода" Margin="5" VerticalAlignment="Center" />
    <Button Content="О программе" x:Name="btAbout" Click="btAbout_Click_1" />
StackPanel>
Добавим еще одну страницу в проект (правый клик на проекте, добавить новый элемент, в левом дереве выбираем Windows Metro Style и в списке кликаем на BlankPage, не забываем переименовать страницу в AboutPage. Заменяем Grid на вот такой StackPanel:

<StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Margin="5" Text="Демонстрационное приложение для показа возможностей навигации в Windows 8 приложениях" />
    <Button Content="На главную" x:Name="btBack" Click="btBack_Click_1" />
StackPanel>
Все, возвращаемся в файл кода главной страницы (MainPage.xaml.cs) и добавляем в обработчик клика на кнопке вот такой код:
 
private void btAbout_Click_1(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(AboutPage));
}

Как видим, наша страница знает про некий Frame и передаем мы ему для навигации не объект новой страницы, а тип, на основе которого он сам создаст страницу. Кстати, у Frame есть еще два полезных метода: GoBack и GoForward. В принципе, именно первым нам и необходимо воспользоваться на странице о программе. Это нам позволит показывать ее не только с главной формы, но и с любой другой, а возврат будет осуществляться на вызвавшею форму. Код обработчика будет иметь вид:

private void btBack_Click_1(object sender, RoutedEventArgs e)
{
    this.Frame.GoBack();
}

Все, если мы запустим приложение, кликнем на кнопку "О программе", то увидим вот такое окно:
Как и любое Metro приложение, наше будет занимать весь экран, а то что мы видим на картинке, только левый верхний угол, но вставлять много красивого черного цвета, я посчитал лишним. Кликнув на кнопку "На главную", благодаря методу GoBack мы вернемся к предыдущей странице.
Второй пример, с передачей параметров, заставит наше предложение поприветствовать нас по имени. В главном окне, мы введем свое имя, а нажав на второй странице, мы должны увидеть "Здравствуй, <введенное имя>".
На MainPage добавляем очередной StackPanel с вот таким кодом:
<StackPanel Orientation="Horizontal">
    <TextBlock Text="Пример передачи параметр. Введите свое имя:" Margin="5" VerticalAlignment="Center" />
    <TextBox x:Name="tbName" Margin="5" MinWidth="200" />
    <Button Content="Привет!" x:Name="btHello" Click="btHello_Click_1" />
StackPanel>
Добавляем в наш проект еще одну страницу с именем HelloPage, на ней также заменим Grid на StackPanel вот с таким кодом:

<StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock x:Name="tbHello" />
    <Button Content="Назад" x:Name="btBack" Click="btBack_Click_1" />
StackPanel>

Теперь, как нам на эту форму передать параметр, на самом деле легко, у метода Navigate есть перегруженная версия принимающая параметр. Ее то мы и вызовем их обработчика кнопки на главной форме:

private void btHello_Click_1(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(HelloPage), tbName.Text);
}
Вполне логично, что если мы сейчас запустим приложение, введем текст и нажмем кнопку "Привет!", то откроется соответствующая страница, вот только на ней кроме кнопки мы ничего не увидим. Ведь параметр мы передали, а вот на второй странице его не обработали. Для обработки параметра, в файле с кодом второй страницы ищем заготовку под метод OnNavigateTo и вставляем в него вот такой код:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    tbHello.Text = string.Format("Здравствуй, {0}", e.Parameter);
}
Все, запускаем приложение, вводим имя, нажимаем кнопку "Привет!" и видим:

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

public MainPage()
{
    this.InitializeComponent();
    this.NavigationCacheMode = NavigationCacheMode.Enabled;
}
Все, теперь при переходе с главной страницы на страницу с приветствием и обратно, мы будем на главной форме видеть введенный ранее текст.
Ну и последний, четвертый пример, в котором мы попробуем передать объект и воспользоваться Binding-ом. Для этого в наш проект добавим вот такой класс:

class Person : DependencyObject
{
    public string FirstName
    {
        get { return (string)GetValue(FirstNameProperty); }
        set { SetValue(FirstNameProperty, value); }
    }
    // Using a DependencyProperty as the backing store for FirstName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FirstNameProperty =
        DependencyProperty.Register("FirstName", typeof(string), typeof(Person), new PropertyMetadata(""));
    public string LastName
    {
        get { return (string)GetValue(LastNameProperty); }
        set { SetValue(LastNameProperty, value); }
    }
    // Using a DependencyProperty as the backing store for LastName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LastNameProperty =
        DependencyProperty.Register("LastName", typeof(string), typeof(Person), new PropertyMetadata(""));
}
На главной форме, добавим в конструктор создание объекта такого класса с помещением его в DataContext нашей страницы:

<StackPanel Orientation="Horizontal" >
    <TextBlock Text="Передача объекта и Binding. Имя:" Margin="5" VerticalAlignment="Center" />
    <TextBlock Text="{Binding FirstName}" Margin="5" MinWidth="200" />
    <TextBlock Text="Фамилия:" Margin="5" VerticalAlignment="Center" />
    <TextBlock Text="{Binding LastName}" Margin="5" MinWidth="200" />
    <Button Content="Редактирование в отдельном окне" x:Name="btEdit" Click="btEdit_Click_1" />
StackPanel>
Добавляем в проект страницу EditPage:

<StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="Имя:" Margin="5" />
    <TextBox Text="{Binding FirstName}" Margin="5" />
    <TextBlock Text="Фамилия:" Margin="5" />
    <TextBox Text="{Binding LastName}" Margin="5" />
    <Button Content="Назад" x:Name="btBack" Click="btBack_Click_1" />
StackPanel>

В ее коде нам нудно только обработчик кнопки задать (GoBack) и присвоение переданного параметра в DataContext:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.DataContext = e.Parameter;
}
Обратили внимание, что в рамках этой формы мы даже не знаем про класс Person? Все, осталось добавить передачу параметра при переходе по кнопке "Редактирование в отдельном окне":
Person person = new Person() { FirstName = "Иван", LastName = "Иванов" };
private void btEdit_Click_1(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(EditPage), person);
}

Запускаем приложение и видим:
Кликаем на кнопке:
Заменяем на Петр Петров, жмем "Назад" и вуаля:
Да, да, вы не ошиблись, опять Иван Иванов. Привет наследие Silverlight, все поля по умолчанию используют тип биндинга: OneWay, придется заменить на TwoWay. Заменяем, запускаем, редактируем, возвращаемся... Не помогает. Придется на главную страницу не просто возвращаться с передачей параметра. На странице редактирования в обработчик кнопки пишем:
private void btBack_Click_1(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(MainPage), this.DataContext);
}
На главной форме в OnNavigateTo, добавляем перезапись DataContext:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (e.Parameter != null)
    {
        this.DataContext = e.Parameter;
    }
}
Все, теперь будет Петр Петров.
Ладно, что то я устал, пойду чайку попью и книжку почитаю. Приятного кодинга.

Комментариев нет:

Отправить комментарий