Реализация навигации между двумя страницами

Узнайте, как использовать кадр и страницы, чтобы включить базовую одноранговую навигацию в приложении.

навигация между одноранговыми элементами

Почти каждое приложение требует навигации между страницами. Даже простое приложение с одной страницей содержимого обычно имеет страницу параметров, требующую навигации. В этой статье мы рассмотрим основы добавления XAML Page в приложение и использования Frame для навигации между страницами.

Важно!

В этом примере используется шаблон Пустое приложение из Microsoft Visual Studio. Существуют различия в шаблонах для приложений Windows App SDK/WinUI 3 и приложений UWP, поэтому не забудьте выбрать правильную вкладку для своего типа приложения.

1. Создайте пустое приложение

Чтобы создать пустое приложение в Visual Studio, выполните следующие действия.

  1. Чтобы настроить среду разработки на компьютере, выполните инструкции из статьи Установка средств для пакета SDK для приложений Windows.
  2. В начальном окне Microsoft Visual Studio выберите Создать проект или в меню Visual Studio выберите Файл>Новый>проект.
  3. В раскрывающихся списках диалогового окна Создание проекта выберите C# или C++, Windows и WinUI соответственно.
  4. Выберите шаблон проекта Blank App, Packaged (WinUI 3 in Desktop) (Пустое, упакованное приложение (WinUI 3 в классических приложениях)) и щелкните Далее. Этот шаблон создает классическое приложение с пользовательским интерфейсом на основе WinUI 3.
  5. В поле Имя проекта введите BasicNavigationи нажмите кнопку Создать.
  6. Чтобы запустить программу, выберите Отладка>Начать отладку в меню или нажмите клавишу F5. Выполните сборку и запустите решение на компьютере для разработки, чтобы убедиться, что приложение выполняется без ошибок. Отобразится пустая страница.
  7. Чтобы остановить отладку и вернуться в Visual Studio, выйдите из приложения или нажмите кнопку Остановить отладку в меню.
  8. Удалите все примеры кода, включенные в шаблон, из MainWindow.xaml файлов кода программной части и MainWindow .

Совет

Дополнительные сведения см. в статье Создание первого проекта WinUI 3 (Windows App SDK).

2. Переход между страницами с помощью фрейма

Если приложение содержит несколько страниц, вы используете frame для перехода между ними. Класс Frame поддерживает различные методы навигации, такие как Navigate, GoBack и GoForward, а также свойства BackStack, ForwardStack и BackStackDepth.

При создании нового проекта Windows App SDK в Visual Studio шаблон проекта создает MainWindow класс (типа Microsoft.UI.Xaml.Window). Однако он не создает фрейм или страницу и не предоставляет код навигации.

Чтобы включить навигацию между страницами, добавьте в Frame качестве корневого MainWindowэлемента . Это можно сделать в переопределении метода Application.OnLaunched в файле кода программной App.xaml части. App Откройте файл кода программной части, обновите OnLaunched переопределение и обработайте событие NavigationFailed, как показано ниже.

// App.xaml.cs

protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
    m_window = new MainWindow();

    // Create a Frame to act as the navigation context and navigate to the first page
    Frame rootFrame = new Frame();
    rootFrame.NavigationFailed += OnNavigationFailed;
    // Navigate to the first page, configuring the new page
    // by passing required information as a navigation parameter
    rootFrame.Navigate(typeof(MainPage), args.Arguments);

    // Place the frame in the current Window
    m_window.Content = rootFrame;
    // Ensure the MainWindow is active
    m_window.Activate();
}

void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
    throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
// App.xaml.h

// Add after OnLaunched declaration.
void OnNavigationFailed(IInspectable const&, Microsoft::UI::Xaml::Navigation::NavigationFailedEventArgs const&);

///////////////
// App.xaml.cpp

void App::OnLaunched(LaunchActivatedEventArgs const& e)
{
    window = make<MainWindow>();
    Frame rootFrame = Frame();
    rootFrame.NavigationFailed({ this, &App::OnNavigationFailed });
    rootFrame.Navigate(xaml_typename<BasicNavigation::MainPage>(), box_value(e.Arguments()));
    window.Content(rootFrame);
    window.Activate();
}

void App::OnNavigationFailed(IInspectable const&, NavigationFailedEventArgs const& e)
{
    throw hresult_error(E_FAIL, hstring(L"Failed to load Page ") + e.SourcePageType().Name);
}

Примечание

Для приложений с более сложной навигацией обычно используется NavigationView в качестве корневого элемента MainWindow и в Frame качестве содержимого представления навигации. Дополнительные сведения см. в разделе Представление навигации.

Метод Navigate используется для отображения содержимого в этом объекте Frame. MainPage.xaml Здесь передается Navigate в метод , поэтому метод загружается MainPageFrameв .

Если переход к начальному окну приложения завершается сбоем NavigationFailed , возникает событие, и этот код создает исключение в обработчике событий.

3. Добавление базовых страниц

Шаблон "Пустое приложение " не создает несколько страниц приложений. Прежде чем переходить между страницами, необходимо добавить несколько страниц в приложение.

Чтобы добавить новый элемент в приложение, выполните указанные далее действия.

  1. В Обозреватель решений щелкните правой BasicNavigation кнопкой мыши узел проекта, чтобы открыть контекстное меню.
  2. Выберите Добавить>новый элемент в контекстном меню.
  3. В диалоговом окне Добавление нового элемента выберите узел WinUI на левой панели, а затем выберите Пустая страница (WinUI 3) в средней области.
  4. В поле Имя введите MainPage и нажмите кнопку Добавить .
  5. Повторите шаги 1–4, чтобы добавить вторую страницу, но в поле Имя введите Page2.

Теперь эти файлы должны быть перечислены как часть проекта BasicNavigation .

C# C++
  • MainPage.xaml
  • MainPage.xaml.cs
  • Page2.xaml
  • Page2.xaml.cs
  • MainPage.xaml
  • MainPage.xaml.cpp
  • MainPage.xaml.h
  • Page2.xaml
  • Page2.xaml.cpp
  • Page2.xaml.h

Важно!

Для проектов C++ необходимо добавить директиву #include в файл заголовка каждой страницы, которая ссылается на другую страницу. В приведенном здесь примере межстраничной навигации файл mainpage.xaml.h содержит #include "Page2.xaml.h", в свою очередь, page2.xaml.h содержит #include "MainPage.xaml.h".

Шаблоны страниц C++ также содержат пример Button и код обработчика щелчков, который необходимо удалить из файлов XAML и программной части для страницы.

Добавление содержимого на страницы

В MainPage.xamlзамените существующее содержимое страницы следующим содержимым:

<Grid>
    <TextBlock x:Name="pageTitle" Text="Main Page"
               Margin="16" Style="{StaticResource TitleTextBlockStyle}"/>
    <HyperlinkButton Content="Click to go to page 2"
                     Click="HyperlinkButton_Click"
                     HorizontalAlignment="Center"/>
</Grid>

Этот КОД XAML добавляет:

  • Элемент TextBlock с именем pageTitle со свойством Text , свойству Main Page Text, присвоено значение в качестве дочернего элемента корневого элемента Grid.
  • Элемент HyperlinkButton , используемый для перехода на следующую страницу в качестве дочернего элемента корневого элемента Grid.

MainPage В файле кода программной части добавьте следующий код для обработки Click события HyperlinkButton, добавленного для включения навигации по Page2.xaml.

// MainPage.xaml.cs

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    Frame.Navigate(typeof(Page2));
}
// pch.h
// Add this include in pch.h to support winrt::xaml_typename

#include <winrt/Windows.UI.Xaml.Interop.h>

////////////////////
// MainPage.xaml.h

void HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);

////////////////////
// MainPage.xaml.cpp

void winrt::BasicNavigation::implementation::MainPage::HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
    Frame().Navigate(winrt::xaml_typename<BasicNavigation::Page2>());
}

MainPage является подклассом класса Page. Класс Page имеет доступное только для чтения свойство Frame , которое получает Frame объект , содержащий Page. Click Когда обработчик HyperlinkButton событий в MainPage вызывает Frame.Navigate(typeof(Page2)), Frame отображает содержимое Page2.xaml.

Всякий раз, когда страница загружается в кадр, эта страница добавляется как PageStackEntry в BackStack или ForwardStackкадра, что позволяет вести журнал и переход в обратном направлении.

Теперь сделайте то же самое в Page2.xaml. Замените существующее содержимое страницы следующим содержимым:

<Grid>
    <TextBlock x:Name="pageTitle" Text="Page 2"
               Margin="16" Style="{StaticResource TitleTextBlockStyle}"/>
    <HyperlinkButton Content="Click to go to main page"
                     Click="HyperlinkButton_Click"
                     HorizontalAlignment="Center"/>
</Grid>

Page2 В файле кода программной части добавьте следующий код для обработки Click события HyperlinkButton для перехода к MainPage.xaml.

// Page2.xaml.cs

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    Frame.Navigate(typeof(MainPage));
}
// Page2.xaml.h

void HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);

/////////////////
// Page2.xaml.cpp

void winrt::BasicNavigation::implementation::Page2::HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
    Frame().Navigate(winrt::xaml_typename<BasicNavigation::MainPage>());
}

Выполните сборку и запуск приложения. Щелкните ссылку «Click to go to page 2» (Нажмите, чтобы перейти к странице 2). Вторая страница с надписью «Page 2» (Страница 2) в верхней части загрузится и появится в фрейме. Теперь щелкните ссылку на странице 2, чтобы вернуться на главную страницу.

4. Передача информации между страницами

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

В MainPage.xamlзамените HyperlinkButton добавленный ранее элемент на следующий StackPanel. При этом добавляется метка TextBlock и TextBoxname для ввода текстовой строки.

<StackPanel VerticalAlignment="Center">
    <TextBlock HorizontalAlignment="Center" Text="Enter your name"/>
    <TextBox HorizontalAlignment="Center" Width="200" x:Name="name"/>
    <HyperlinkButton Content="Click to go to page 2"
                              Click="HyperlinkButton_Click"
                              HorizontalAlignment="Center"/>
</StackPanel>

Теперь вы используете вторую перегрузку Navigate метода и передайте текст из текстового поля в качестве второго параметра. Вот сигнатура этой Navigate перегрузки:

public bool Navigate(System.Type sourcePageType, object parameter);
bool Navigate(TypeName const& sourcePageType, IInspectable const& parameter);

В обработчике HyperlinkButton_Click событий файла кода программной MainPage части добавьте второй параметр в Navigate метод, который ссылается на Text свойство текстового name поля.

// MainPage.xaml.cs

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    Frame.Navigate(typeof(Page2), name.Text);
}
// MainPage.xaml.cpp

void winrt::BasicNavigation::implementation::MainPage::HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{ 
    Frame().Navigate(xaml_typename<BasicNavigation::Page2>(), winrt::box_value(name().Text()));
}

В Page2.xamlзамените добавленный HyperlinkButton ранее на следующий StackPanel. При этом будет добавлен элемент TextBlock для отображения текстовой строки, передаваемой из MainPage.

<StackPanel VerticalAlignment="Center">
    <TextBlock HorizontalAlignment="Center" x:Name="greeting"/>
    <HyperlinkButton Content="Click to go to page 1"
                     Click="HyperlinkButton_Click"
                     HorizontalAlignment="Center"/>
</StackPanel>

Page2 В файле кода программной части добавьте следующий код, чтобы переопределить OnNavigatedTo метод :

// Page2.xaml.cs

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (e.Parameter is string && !string.IsNullOrWhiteSpace((string)e.Parameter))
    {
        greeting.Text = $"Hello, {e.Parameter.ToString()}";
    }
    else
    {
        greeting.Text = "Hello!";
    }
    base.OnNavigatedTo(e);
}
// Page2.xaml.h

void Page2::OnNavigatedTo(Microsoft::UI::Xaml::Navigation::NavigationEventArgs const& e)
{
	auto propertyValue{ e.Parameter().as<Windows::Foundation::IPropertyValue>() };
	if (propertyValue.Type() == Windows::Foundation::PropertyType::String)
	{
		auto name{ winrt::unbox_value<winrt::hstring>(e.Parameter()) };
		if (!name.empty())
		{
			greeting().Text(L"Hello, " + name);
			__super::OnNavigatedTo(e);
			return;
		}
	}
	greeting().Text(L"Hello!");
	__super::OnNavigatedTo(e);
}

Запустите приложение, введите свое имя в текстовое поле и щелкните ссылку с надписью Click to go to page 2.

Click Когда событие HyperlinkButton in вызывает Frame.Navigate(typeof(Page2), name.Text), name.Text свойство передается Page2в MainPage , а значение из данных события используется для сообщения, отображаемого на странице.

5. Кэширование страницы

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

В нашем базовом одноранговом примере при щелчке Click to go to page 1 по ссылке TextBox в Page2параметре (и любом другом поле) в MainPage устанавливается состояние по умолчанию. Один способов обойти эту проблему — использовать свойство NavigationCacheMode, чтобы указать, что страница будет добавлена в кэш страницы фрейма.

По умолчанию при каждом переходе создается новый экземпляр страницы со значениями по умолчанию. В MainPage.xamlзадайте значение NavigationCacheModeEnabled (в открывающем Page теге), чтобы кэшировать страницу и сохранять все значения содержимого и состояния страницы до тех пор, пока не будет превышен кэш страницы для кадра. Задайте параметру NavigationCacheMode значение Required, если требуется игнорировать ограничения CacheSize, обозначающие число страниц в журнале навигации, которые можно кэшировать для кадра. Однако следует помнить, что ограничения размера кэша могут быть критическими в зависимости от ограничений памяти устройства.

<Page
    x:Class="BasicNavigation.MainPage"
    ...
    mc:Ignorable="d"
    NavigationCacheMode="Enabled">

Теперь, когда вы щелкаете main страницу, имя, которое вы ввели в текстовом поле, по-прежнему будет там.

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

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

Эти анимации представлены подклассами NavigationTransitionInfo. Чтобы указать анимацию для перехода страницы, используйте третью перегрузку Navigate метода и передайте NavigationTransitionInfo подкласс в качестве третьего параметра (infoOverride). Вот сигнатура этой Navigate перегрузки:

public bool Navigate(System.Type sourcePageType, 
                     object parameter,
                     NavigationTransitionInfo infoOverride);
bool Navigate(TypeName const& sourcePageType, 
              IInspectable const& parameter, 
              NavigationTransitionInfo const& infoOverride);

В обработчике HyperlinkButton_Click событий файла кода программной MainPage части добавьте третий параметр в Navigate метод , который задает infoOverride параметру Значение SlideNavigationTransitionInfo , а свойству Effect присвоено значение FromRight.

// MainPage.xaml.cs

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    Frame.Navigate(typeof(Page2), 
                   name.Text,
                   new SlideNavigationTransitionInfo() 
                       { Effect = SlideNavigationTransitionEffect.FromRight});
}
// pch.h

#include <winrt/Microsoft.UI.Xaml.Media.Animation.h>

////////////////////
// MainPage.xaml.cpp

using namespace winrt::Microsoft::UI::Xaml::Media::Animation;

// ...

void winrt::BasicNavigation::implementation::MainPage::HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{   
    // Create the slide transition and set the transition effect to FromRight.
    SlideNavigationTransitionInfo slideEffect = SlideNavigationTransitionInfo();
    slideEffect.Effect(SlideNavigationTransitionEffect(SlideNavigationTransitionEffect::FromRight));
    Frame().Navigate(winrt::xaml_typename<BasicNavigation::Page2>(),
        		     winrt::box_value(name().Text()),
                     slideEffect);
}

В обработчике HyperlinkButton_Click событий файла кода программной Page2 части задайте infoOverride параметру Значение SlideNavigationTransitionInfo , а свойству Effect присвоено значение FromLeft.

// Page2.xaml.cs

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    Frame.Navigate(typeof(MainPage),
                   null,
                   new SlideNavigationTransitionInfo() 
                       { Effect = SlideNavigationTransitionEffect.FromLeft});
}
// Page2.xaml.cpp

using namespace winrt::Microsoft::UI::Xaml::Media::Animation;

// ...

void winrt::BasicNavigation::implementation::MainPage::HyperlinkButton_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{   
    // Create the slide transition and set the transition effect to FromLeft.
    SlideNavigationTransitionInfo slideEffect = SlideNavigationTransitionInfo();
    slideEffect.Effect(SlideNavigationTransitionEffect(SlideNavigationTransitionEffect::FromLeft));
    Frame().Navigate(winrt::xaml_typename<BasicNavigation::MainPage>(),
        		     nullptr,
                     slideEffect);
}

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