Иерархическая навигация
Класс NavigationPage обеспечивает иерархическую навигацию, при которой пользователь может переходить по страницам вперед и назад по своему желанию. Этот класс реализует навигацию на основе стека объектов Page по методу ЛИФО (последним поступил — первым обслужен). В этой статье показано, как использовать класс NavigationPage для навигации в стеке страниц.
Для перехода с одной страницы на другую приложение помещает новую страницу в стек навигации, где она становится активной страницей, как показано на следующем рисунке.
Для возврата к предыдущей странице приложение выбирает текущую страницу из стека навигации, после чего активной становится верхняя страница в стеке, как показано на следующем рисунке.
Методы навигации предоставляются свойством Navigation
любых типов, производных от класса Page
. Эти методы предоставляют возможность для отправки страниц в стек навигации, извлечения страниц из стека навигации, а также для выполнения операций со стеком.
Выполнение навигации
При иерархической навигации класс NavigationPage
используется для перехода по стеку объектов ContentPage
. На следующих снимках экрана показаны основные компоненты NavigationPage
на каждой платформе.
Макет NavigationPage
зависит от платформы:
- В iOS панель навигации находится в верхней части страницы, где отображается заголовок и кнопка Назад, которая возвращает на предыдущую страницу.
- В Android панель навигации находится в верхней части страницы, где отображается заголовок, значок и кнопка Назад, которая возвращает на предыдущую страницу. Значок определяется в атрибуте
[Activity]
, который оформляет классMainActivity
в проекте, зависящем от платформы Android. - На универсальной платформе Windows панель навигации расположена в верхней части страницы, где отображается заголовок.
На всех платформах значение свойства Page.Title
отображается как заголовок страницы. Кроме того, свойству IconColor
можно задать значение Color
, которое применяется к значку на панели навигации.
Примечание.
Рекомендуется заполнять NavigationPage
только экземплярами ContentPage
.
Создание корневой страницы
Первая страница, добавленная в стек навигации, называется корневой страницей приложения, что демонстрируется в следующем примере кода:
public App ()
{
MainPage = new NavigationPage (new Page1Xaml ());
}
В результате в стек навигации помещается экземпляр Page1Xaml
ContentPage
, где он становится активной страницей и корневой страницей приложения. Эти действия показаны на следующих снимках экрана:
Примечание.
Свойство RootPage
экземпляра NavigationPage
предоставляет доступ к первой странице в стеке навигации.
Помещение страниц в стек навигации
Для перехода к странице Page2Xaml
необходимо вызвать метод PushAsync
свойства Navigation
текущей страницы, как показано в следующем примере кода.
async void OnNextPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PushAsync (new Page2Xaml ());
}
В результате в стек навигации помещается экземпляр Page2Xaml
, где он становится активной страницей. Эти действия показаны на следующих снимках экрана:
Когда вызывается метод PushAsync
, происходят следующие события.
- У страницы, вызывающей
PushAsync
, вызывается переопределениеOnDisappearing
. - Вызывается переопределение
OnAppearing
страницы, к которой осуществляется переход. - Задача
PushAsync
завершается.
Однако точный порядок, в котором происходят эти события, зависит от платформы. Дополнительные сведения см. в главе 24 книги о Xamarin.Forms Чарльза Петцольда (Charles Petzold).
Примечание.
Вызовы переопределений OnDisappearing
и OnAppearing
не могут рассматриваться как гарантия перехода на страницу. Например, в iOS переопределение OnDisappearing
вызывается на активной странице, когда приложение завершает работу.
Извлечение страниц из стека навигации
Активная страница может быть извлечена из стека навигации путем нажатия кнопки Назад на устройстве, причем это может быть как физическая кнопка, так и кнопка на экране.
Чтобы вернуться на исходную страницу программным образом, экземпляр Page2Xaml
должен вызвать метод PopAsync
, как показано в следующем примере кода:
async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PopAsync ();
}
В результате экземпляр Page2Xaml
удаляется из стека навигации, а активной становится верхняя страница в нем. Когда вызывается метод PopAsync
, происходят следующие события.
- У страницы, вызывающей
PopAsync
, вызывается переопределениеOnDisappearing
. - У страницы, к которой вы возвращаетесь, вызывается переопределение
OnAppearing
. - Возвращается задача
PopAsync
.
Однако точный порядок, в котором происходят эти события, зависит от платформы. Дополнительные сведения см. в главе 24 книги о Xamarin.Forms Чарльза Петцольда (Charles Petzold).
Как и методы PushAsync
и PopAsync
, свойство Navigation
каждой страницы также предоставляет метод PopToRootAsync
, который показан в следующем примере кода.
async void OnRootPageButtonClicked (object sender, EventArgs e)
{
await Navigation.PopToRootAsync ();
}
Этот метод извлекает все, кроме корневого объекта Page
, из стека навигации; таким образом, корневая страница приложения становится активной страницей.
Анимация переходов по страницам
Свойство Navigation
каждой страницы также предоставляет переопределенные методы отправки и извлечения, которые включают параметр boolean
, указывающий, нужно ли отображать анимацию страниц во время перехода, как показано в следующем примере кода.
async void OnNextPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PushAsync (new Page2Xaml (), false);
}
async void OnPreviousPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PopAsync (false);
}
async void OnRootPageButtonClicked (object sender, EventArgs e)
{
// Page appearance not animated
await Navigation.PopToRootAsync (false);
}
Установка для параметра boolean
значения false
отключает анимацию перехода страницы, а установка для параметра значения true
включает анимацию, при условии что она поддерживается используемой платформой. Однако методы отправки и извлечения без этого параметра включают анимацию по умолчанию.
Передача данных при переходе
Иногда странице необходимо передать данные другой странице во время навигации. Существует два способа: передача данных с помощью конструктора страниц и указание данных для объекта BindingContext
новой страницы. Мы обсудим оба способа.
Передача данных через конструктор страниц
Самый простой способ передачи данных на другую страницу во время навигации — в качестве параметра конструктора страниц, как показано в следующем примере кода:
public App ()
{
MainPage = new NavigationPage (new MainPage (DateTime.Now.ToString ("u")));
}
Этот код создает экземпляр MainPage
, передавая текущую дату и время в формате ISO8601, который упаковывается в экземпляр NavigationPage
.
Экземпляр MainPage
получает данные с помощью параметра конструктора, как показано в следующем примере кода.
public MainPage (string date)
{
InitializeComponent ();
dateLabel.Text = date;
}
Данные отображаются на странице путем установки свойства Label.Text
, как показано на следующих снимках экрана.
Передача данных через объект BindingContext
Альтернативный способ передачи данных на другую страницу во время навигации —указание данных для объекта BindingContext
новой страницы, как показано в следующем примере кода:
async void OnNavigateButtonClicked (object sender, EventArgs e)
{
var contact = new Contact {
Name = "Jane Doe",
Age = 30,
Occupation = "Developer",
Country = "USA"
};
var secondPage = new SecondPage ();
secondPage.BindingContext = contact;
await Navigation.PushAsync (secondPage);
}
Этот код задает объекту BindingContext
экземпляра SecondPage
экземпляр Contact
, а затем переходит к SecondPage
.
Затем SecondPage
использует привязку данных для отображения данных экземпляра Contact
, как показано в следующем примере кода XAML.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PassingData.SecondPage"
Title="Second Page">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<Label Text="Name:" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
</StackLayout>
...
<Button x:Name="navigateButton" Text="Previous Page" Clicked="OnNavigateButtonClicked" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
В следующем примере кода показано, как можно выполнить привязку данных в C#.
public class SecondPageCS : ContentPage
{
public SecondPageCS ()
{
var nameLabel = new Label {
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
FontAttributes = FontAttributes.Bold
};
nameLabel.SetBinding (Label.TextProperty, "Name");
...
var navigateButton = new Button { Text = "Previous Page" };
navigateButton.Clicked += OnNavigateButtonClicked;
Content = new StackLayout {
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Children = {
new StackLayout {
Orientation = StackOrientation.Horizontal,
Children = {
new Label{ Text = "Name:", FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)), HorizontalOptions = LayoutOptions.FillAndExpand },
nameLabel
}
},
...
navigateButton
}
};
}
async void OnNavigateButtonClicked (object sender, EventArgs e)
{
await Navigation.PopAsync ();
}
}
Данные отображаются на странице с помощью нескольких элементов управления Label
, как показано на следующих снимках экрана.
Дополнительные сведения о привязке данных см. в статье Основы привязки данных.
Управление стеком навигации
Свойство Navigation
предоставляет свойство NavigationStack
, из которого могут быть получены страницы в стеке навигации. Хотя Xamarin.Forms поддерживает доступ к стеку навигации, свойство Navigation
предоставляет методы InsertPageBefore
и RemovePage
для управления стеком путем вставки страниц или их удаления.
Метод InsertPageBefore
вставляет указанную страницу в стек навигации перед указанной существующей страницей, как показано на следующей схеме.
Метод RemovePage
удаляет указанную страницу из стека навигации, как показано на следующей схеме.
Эти методы обеспечивают возможность настраиваемой навигации, например замены страницы входа на новую страницу после успешного входа. Следующий пример кода демонстрирует этот сценарий.
async void OnLoginButtonClicked (object sender, EventArgs e)
{
...
var isValid = AreCredentialsCorrect (user);
if (isValid) {
App.IsUserLoggedIn = true;
Navigation.InsertPageBefore (new MainPage (), this);
await Navigation.PopAsync ();
} else {
// Login failed
}
}
При условии что учетные данные пользователя верны, экземпляр MainPage
вставляется в стек навигации перед текущей страницей. Метод PopAsync
удаляет текущую страницу из стека навигации, а активной страницей становится экземпляр MainPage
.
Отображение представлений на панели навигации
Любой Xamarin.FormsView
объект можно отобразить на панели навигации.NavigationPage
Для этого нужно установить присоединенное свойство NavigationPage.TitleView
в View
. Это присоединенное свойство может быть задано для любого объекта Page
, и, когда Page
помещается в NavigationPage
, NavigationPage
будет учитывать значение этого свойства.
В следующем примере показано, как задать присоединенное NavigationPage.TitleView
свойство из XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NavigationPageTitleView.TitleViewPage">
<NavigationPage.TitleView>
<Slider HeightRequest="44" WidthRequest="300" />
</NavigationPage.TitleView>
...
</ContentPage>
Вот эквивалент в коде C#:
public class TitleViewPage : ContentPage
{
public TitleViewPage()
{
var titleView = new Slider { HeightRequest = 44, WidthRequest = 300 };
NavigationPage.SetTitleView(this, titleView);
...
}
}
В результате объект Slider
отображается на панели навигации на NavigationPage
:
Внимание
Многие представления не будут отображаться на панели навигации, если не указан размер представления с помощью свойств WidthRequest
и HeightRequest
. Кроме того, представление может быть заключено в StackLayout
со свойствами HorizontalOptions
и VerticalOptions
, для которых установлены соответствующие значения.
Поскольку класс Layout
является производным от класса View
, присоединенное свойство TitleView
можно настроить для отображения класса макета, содержащего несколько представлений. В iOS и на универсальной платформе Windows (UWP) высоту панели навигации нельзя изменить, поэтому она будет обрезана, если представление, отображаемое на панели навигации, больше, чем размер панели навигации по умолчанию. В Android высоту панели навигации можно изменить, задав для привязываемого свойства NavigationPage.BarHeight
значение double
, представляющее новую высоту. Дополнительные сведения см. в разделе Установка высоты панели навигации в объекте NavigationPage.
Также панель навигации можно расширить, если поместить некоторое содержимое на панели навигации, а некоторое — в представление в верхней части страницы таким образом, чтобы цвет совпадал с панелью навигации. Кроме того, в iOS разделительную линию и тень в нижней части панели навигации можно удалить, установив для привязываемого свойства NavigationPage.HideNavigationBarSeparator
значение true
. Дополнительные сведения см. в разделе Сокрытие разделителя панели навигации в объекте NavigationPage.
Примечание.
Свойства BackButtonTitle
, Title
, TitleIcon
и TitleView
позволяют определять значения, которые занимают место на панели навигации. Хотя размер панели навигации зависит от платформы и размера экрана, установка всех этих свойств приведет к конфликтам из-за ограничений свободного пространства. Вместо комбинации этих свойств лучше задать желаемый дизайн панели навигации только с помощью свойства TitleView
.
Ограничения
Существует ряд ограничений, которые следует учитывать при отображении объекта View
на панели навигации NavigationPage
.
- В iOS представления, размещенные на панели навигации
NavigationPage
, отображаются по-разному в зависимости от того, включены ли крупные заголовки. Дополнительные сведения о включении крупных заголовков см. в разделе Отображение крупных заголовков. - В Android можно поместить представления на панели навигации
NavigationPage
только в приложениях, использующих совместимость приложений. - Не рекомендуется помещать большие и сложные представления, например
ListView
иTableView
, в строке навигацииNavigationPage
.