Приложения Silverlight Windows Phone имеют модель навигации в стиле веб-страниц. Для простоты перехода на предыдущие страницы в устройстве предусмотрена аппаратная кнопка Back (чтобы не занимать драгоценное место на экране), а журнал (или история) ваших переходов интегрирован с платформой, что облегчает навигацию или переходы между разными приложениями. Эта статья из двух частей посвящена следующим вопросам.
- Что представляет собой модель навигации по страницам в Windows Phone.
- Как выжать максимум из текущих API, в том числе как интегрировать свое приложение с аппаратной кнопкой Back, оптимизировать загрузку и выгрузку страниц, а также убедиться, что ваша модель навигации отвечает правилам сертификации для Windows Phone.
- Как создавать самые сложные системы навигации, не реализованные с помощью текущих API.
Модель навигации в Windows Phone
Эта модель включает рамку (PhoneApplicationFrame) и одну или более страниц (PhoneApplicationPage), в которых хранится содержимое, загружаемое в рамку.
PhoneApplicationFrame предоставляет большую часть навигационных событий и метод Navigate для перехода между страницами. Он также определяет клиентскую область приложения на экране и резервирует часть места под панель приложения (application bar) и системный лоток (system tray).
PhoneApplicationPage поддерживает уведомления, специфичные для страниц; они генерируются, когда происходит переход на какую-либо страницу или когда пользователь покидает страницу. Он также обрабатывает события, связанные с аппаратной кнопкой Back.
PhoneApplicationFrame и PhoneApplicationPage совместно используют NavigationService; реальный процесс навигации выполняется этим сервисом. Windows Phone поддерживает ведение журнала (отслеживает историю загружавшихся страниц, благодаря чему вы можете вернуться на предыдущую страницу) и предоставляет соответствующие API. Навигацию в направлении вперед устройства с Windows Phone не поддерживают.
В устройствах с Windows Phone есть три выделенные аппаратные кнопки: Back, Start и Search. Для сертификации приложение должно отвечать определенным требованиям, связанным с обработкой аппаратной кнопки Back.
- Приложение не должно препятствовать попыткам пользователя вернуться на предыдущую страницу. Единственное исключение — страница запроса данных, которые можно потерять при возврате на предыдущую страницу; в этом случае следует запрашивать подтверждение и при положительном ответе разрешать пользователю переходить в обратном направлении.
- Если открывается всплывающее окно, панель программного ввода (Software Input Panel, SIP) или другой временный диалог, нажатие аппаратной кнопки Back должно приводить к удалению соответствующего окна, но не к выходу с текущей страницы.
- Нажатие на кнопку Back при нахождении на первом экране приложения должно приводить к выходу их этого приложения. Эта функциональность не требует никаких усилий от разработчика. Если вы не блокируете эту возможность, инфраструктура обеспечивает выход из приложения за вас — фактически это единственный способ выхода из приложения Silverlight. В предоставляемых API нет метода Exit.
- Для поддержания единого стиля UI между приложениями, кнопку Back следует использовать только для навигации в направлении назад.
Помимо кнопки Back в навигации важную роль играет кнопка Start. Когда пользователь нажимает кнопку Start, выполняемое приложение деактивируется и происходит переключение контекста для перехода в меню Start. Отсюда пользователь может запустить другое приложение и переходить по страницам в рамках этого нового приложения либо вернуться в ранее выполнявшееся приложение (нажатием аппаратной кнопки Back). В конечном счете это создает модель навигации, где кнопка Back обеспечивает переход по страницам выполняемого в данный момент приложения или по стеку ранее запущенных приложений.
Windows Phone API
Как упоминалось, центральные роли в навигации играют PhoneApplicationFrame и PhoneApplicationPage.
PhoneApplicationFrame действует как RootVisual для приложения. При запуске в классе App из App.xaml.cs создается экземпляр PhoneApplicationFrame (рис. 1).
Рис. 1. Создание экземпляра RootFrame в App.xaml.cs
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Create the frame but don't set it as RootVisual yet; this allows the splash
// screen to remain active until the application is ready to render.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Handle navigation failures
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Ensure we don't initialize again
phoneApplicationInitialized = true;
}
Исполняющая среда осуществляет автоматическую навигацию в экземпляр PhoneApplicationPage, который указывается атрибутом NavigationPage в DefaultTask в манифесте WMAppManifest.xml приложения:
<Tasks>
<DefaultTask Name ="_default" NavigationPage="MainPage.xaml"/>
</Tasks>
PhoneApplicationFrame предоставляет большую часть методов и событий навигации, которые понадобятся нам в этой статье. В табл. 2 перечислены наиболее релевантные методы, свойства и события PhoneApplicationFrame.
Табл. 2. Методы, свойства и события PhoneApplicationFrame
Имя | Тип | Описание |
Navigate | Метод | Переходит к новому PhoneApplicationPage, заданному параметром URI. Поскольку этот параметр содержит URI, то вызов Navigate в конечном счете приводит к созданию экземпляра новой страницы и переходу на нее (уже созданная страница не передается). |
CanGoBack | Свойство только для чтения | Возвращает true, если история переходов приложения не пуста. Это означает, что пользователь может переходить в направлении вперед минимум один раз в пределах приложения. Если приложение находится на первой странице, CanGoBack возвращает false и вы не сможете программно вызвать GoBack, но конечный пользователь все равно сможет нажать аппаратную кнопку Back и выйти из приложения, вернувшись в ранее запущенное приложение |
CanGoForward | Свойство только для чтения | К Windows Phone не применимо. Всегда возвращает false, так как навигация в направлении вперед не поддерживается |
UriMapper | Свойство | Возвращает/задает UriMapper. Его рассмотрение выходит за рамки этой статьи, однако стоит упомянуть, что в Windows Phone поддерживается сопоставление URI (URI mapping) |
GoBack | Метод | Переходит к наиболее недавнему элементу в стеке переходов назад (back stack). Этот метод генерирует исключение, если в таком стеке нет элементов; перед вызовом этого метода всегда проверяйте значение свойства CanGoForward |
GoForward | Метод | Не поддерживается в Windows Phone; генерирует исключение InvalidOperationException |
Navigating | Событие | Возникает при запросе новой операции навигации. В этот момент его еще можно отменить, установив свойство Cancel в параметре NavigatingCancelEventArgs в true. Однако отменять навигацию в этом событии не следует.Почему — мы поясним позже |
Navigated | Событие | Возникает после выполнения операции навигации. Это не значит, что уже загружен контент той страницы, на которую произошел переход Возникает при обнаружении контента и перехода к нему. |
NavigationFailed | Событие | Возникает при ошибке навигации |
NavigationStopped | Событие | Возникает при остановке навигации либо вызовом метода StopLoading, либо из-за запроса новой операции навигации в то время, когда текущая операция навигации еще не завершена |
В основном наследование осуществляется от Frame, поэтому тем, кто знает Silverlight-класс Frame, эти методы должны быть знакомы. В табл. 2 перечислены не все члены PhoneApplicationFrame, а только относящиеся к навигации.
Просмотр кода Чтобы увидеть все события и свойства в действии, изучите AllNavigationsEvents.xaml.cs в примере кода, прилагаемом к этой статье. Порядок, в котором срабатывают события, показан на рис. 3.
Рис. 3. Последовательность событий при навигации по страницам
PhoneApplicationFrame также определяет клиентскую область, которую получит приложение, и резервирует место под панель приложения и системный лоток. Эта деталь важна при навигации по страницам, на которых присутствует панель приложения, поскольку она задается на уровне страниц и, кроме того, в Windows Phone имеется общесистемная анимация, позволяющая отображать и скрывать эту панель при загрузке приложения.
Второй участник в процессе навигации — PhoneApplicationPage. На него возлагаются две крайне важные задачи:
- обработка нажатий аппаратной кнопки Back;
- предоставление событий в жизненном цикле страницы, позволяющих определять, когда страница активируется или деактивируется.
Для интеграции с аппаратной кнопкой Back в PhoneApplicationPage предусмотрено событие BackKeyPress. В классе страницы также есть виртуальный метод OnBackKeyPress, который вы можете переопределить в своем экземпляре страницы для обработки и даже для отмены события нажатия кнопки Back.
PhoneApplicationFrame имеет событие Navigating и уведомление/обратный вызов OnNavigatingFrom. Используя эти пары, вы можете отменять переходы на другие страницы в рамках приложения; для этого нужно присвоить e.Cancel значение true в параметре NavigationCancelEventArgs, передаваемом этим методам.Из-за известной ошибки платформы вы не должны отменять операции навигации по нажатию кнопки Back из этих событий/методов. Если вы все же отмените нажатие аппаратной кнопки Back в этом событии, ваша система навигации может быть разрушена и приложение придется перезапускать. Отменять нажатие аппаратной кнопки Back рекомендуется лишь в событии BackKeyPress и обратном вызове OnBackKeyPress.
Список событий и методов, в которых можно отменять операции навигации, с рекомендациями о том, можно ли в конкретном случае отменять нажатие кнопки Back и как проверять, было ли событие связано с навигацией в направлении назад, см. в табл. 4.
Табл. 4. События и методы, где возможна отмена операций навигации
Владелец | Событие/Уведомление | Можно ли отменять новую навигацию | Можно ли отменять переходы по Back | Проверка переходов в направлении назад |
PhoneApplicationFrame | Navigating | Да | Нет | Проверяйте e.NavigationMode != NavigationMode.Back |
PhoneApplicationPage | OnNavigatingFrom | Да | Нет | Проверяйте e.NavigationMode != NavigationMode.Back |
PhoneApplicationPage | OnBackKeyPress | Нет (вызывается, только когда нажимается кнопка Back) | Да | Не требуется; вызывается только при нажатии аппаратной кнопки Back |
PhoneApplicationPage | BackKeyPress (событие) | Нет (вызывается, только когда нажимается кнопка Back) | Да | Не требуется; вызывается только при нажатии аппаратной кнопки Back |
PhoneApplicationPage дополняет эти события более полезными для страницы методами обратного вызова OnNavigatedTo и OnNavigatedFrom. Чтобы лучше понимать и запоминать, когда вызываются эти обратные вызовы, завершайте имена таких методов словами «this page» («эта страница»). Первый из методов вызывается, когда пользователь «перешел на эту страницу», а второй — когда пользователь «переходит с этой страницы» на другую.
Последовательность событий при навигации по страницам показана на рис. 3. Пара методов NavigatedTo/NavigatedFrom идеальна для начала и окончания работы, необходимой, когда страница видима, но они не требуются, если страница находится в стеке переходов назад. Также заметьте, что NavigatedTo всегда срабатывает до загрузки страницы, поэтому не полагайтесь на то, что в этот момент содержимое страницы уже загружено.
Методы OnNavigatedTo и OnNavigatedFrom критически важны в Windows Phone из-за наличия стека переходов назад. ОС поддерживает этот стек для страниц, на которые вы можете вернуться, поэтому страницы не выгружаются, не уничтожаются и не попадают под сбор мусора немедленно при переходе с одной страницы на другую. Вместо этого страницы перемещаются в стек переходов назад и хранятся в памяти.Когда пользователь возвращается к одной из таких страниц, она просто добавляется обратно в визуальное дерево. Страница не создается заново (если только приложение не было деактивировано и уничтожено за тот промежуток между моментом, когда пользователь покинул страницу, и временем, когда он решил вернуться на нее). Поскольку журнал навигации в направлении вперед не поддерживается, страницы могут подпасть под сбор мусора, когда пользователь переходит с текущей страницы обратно на предыдущую при условии, что других ссылок на эту страницу нет.
Схема, иллюстрирующая жизненный цикл PhoneApplicationPage, показана на рис. 5.
Рис. 5. Жизненный цикл PhoneApplicationPage
Пока вы переходите с Page1 на Page2, а потом на Page3, страницы не включаются в сбор мусора, если только вы не вызываете метод GoBack со страницы. Неактивные страницы помещаются в стек переходов назад, но все еще находятся в памяти. Если эти страницы прослушивают глобальные события, то слушатели событий по-прежнему активны.
Хотя страница не подпадает под сбор мусора, когда вы уходите с нее, она больше не видима и не активна до тех пор, пока вы не вернетесь на нее, поэтому вы должны заботиться об очистке и освобождении любых дорогостоящих ресурсов, когда пользователь переходит с данной страницы на другую. Например, если вы прослушиваете изменения в местонахождении, используя GeoCoordinateWatcher, то должны останавливать слушатель OnNavigatedFrom и перезапускать его, когда пользователь возвращается на вашу страницу и вызывается OnNavigatedTo этой страницы.
Просмотр кода Чтобы увидеть, как страницы хранятся в памяти, пока они находятся в стеке переходов назад, изучите страницу GarbageCollectedSample, включенную в сопутствующий этой статье исходный код. Ряд страниц остается в памяти, и это число увеличивается при навигации между страницами.
На этом позвольте нам закончить первую часть статьи. В следующий раз мы сосредоточимся на более продвинутой тематике, относящейся к навигации.