Язык

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

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

одноранговая навигация

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

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

Чтобы создать пустое приложение в Visual Studio:

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

2. Использование фрейма для перехода между страницами

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

При создании нового Windows App SDK project в Visual Studio шаблон project создает класс 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>();
    Microsoft::UI::Xaml::Controls::Frame rootFrame = Microsoft::UI::Xaml::Controls::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, поэтому он загружает MainPage в Frame.

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

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

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

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

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

Tip

Используйте шаблон элемента Пустая страница для добавления страниц. Этот шаблон устанавливает для XAML-файла значение Build Action — Page, что приводит к созданию частичного класса страницы и метода InitializeComponent, чтобы XAML-файл был связан с соответствующим программным кодом. Если вы добавите страницу другим способом и компилятор сообщит, что InitializeComponent не определен, выберите XAML-файл в Обозреватель решений и убедитесь, что для его свойства Действие при сборке задано значение Page.

Теперь эти файлы должны быть перечислены как часть вашего 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, используется как дочерний элемент корневого Grid.
  • Элемент HyperlinkButton, используемый для навигации к следующей странице, являющийся дочерним элементом корневого Grid.

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

// 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/Microsoft.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)) отображает содержимое FramePage2.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>());
}

Создайте и запустите приложение. Щелкните ссылку, которая говорит "Щелкните, чтобы перейти на страницу 2". Вторая страница, с указанием "Страница 2" вверху, должна быть загружена и отображаться в кадре. Теперь щелкните ссылку на странице 2, чтобы вернуться на главную страницу.

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

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

Замените в MainPage.xamlHyperlinkButton, который вы добавили ранее, на следующий элемент StackPanel. При этом добавляется метка TextBlock и текстовое полеname для ввода текстовой строки.

<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 перегрузки:

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

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

// 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 вызывает MainPage, свойство Frame.Navigate(typeof(Page2), name.Text) передается в name.Text, и значение из данных события используется для сообщения, отображаемого на странице.

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

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

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

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

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

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

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::Page2::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);
}

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