Создание приложения .NET MAUI
В этой серии учебников показано, как создать приложение пользовательского интерфейса многоплатформенного приложения .NET (.NET MAUI), использующее только кроссплатформенный код. Это означает, что код, который вы пишете, не зависит от Windows, Android, iOS или macOS. Приложение, которое вы создадите, будет приложением для заметок, где пользователь может создавать, сохранять и загружать несколько заметок.
В этом руководстве описано следующее:
- Создайте приложение оболочки .NET MAUI Shell.
- Запустите приложение на выбранной платформе.
- Определите пользовательский интерфейс с помощью языка разметки приложений eXtensible (XAML) и взаимодействуйте с элементами XAML с помощью кода.
- Создайте представления и привязите их к данным.
- Используйте навигацию для перехода к страницам и с нее.
Вы будете использовать Visual Studio 2022 для создания приложения, с помощью которого можно ввести заметку и сохранить его в хранилище устройств. Ниже показано итоговое приложение:
Создание проекта
Прежде чем приступить к работе с этим руководством, необходимо выполнить сборку первой статьи приложения. При создании проекта используйте следующие параметры:
Имя проекта
Это должно быть присвоено объекту
Notes
. Если проект называется другим, код, скопированный в этом руководстве, может привести к ошибкам сборки.Поместите решение и проект в один каталог
Снимите флажок этого параметра.
При создании проекта выберите последнюю версию платформы .NET Framework.
Выбор целевого устройства
Приложения .NET MAUI предназначены для работы на нескольких операционных системах и устройствах. Вам потребуется выбрать целевой объект, с помощью которого вы хотите протестировать и отладить приложение.
Установите целевой объект отладки на панели инструментов Visual Studio на устройство, с которым требуется выполнить отладку и тестирование. Ниже показано, как задать целевой объект отладки для Android:
- Нажмите кнопку раскрывающегося списка "Целевой объект отладки".
- Выберите элемент Эмуляторов Android.
- Выберите устройство эмулятора.
Настройка оболочки приложения
При создании проекта MAUI для .NET Visual Studio создаются четыре важных файла кода. Их можно увидеть в области Обозреватель решений Visual Studio:
Эти файлы помогают настроить и запустить приложение .NET MAUI. Каждый файл служит другой целью, описанной ниже:
MauiProgram.cs
Это файл кода, который загружает приложение. Код в этом файле служит кроссплатформенной точкой входа приложения, которая настраивает и запускает приложение. Код запуска шаблона указывает на класс, определенный
App
файлом App.xaml .App.xaml и App.xaml.cs
Просто для простоты оба этих файла называются одним файлом. Как правило, есть два файла с любым XAML-файлом, сам XAML-файл и соответствующим файлом кода, который является дочерним элементом в Обозреватель решений. Xaml-файл содержит разметку XAML, а файл кода содержит код, созданный пользователем для взаимодействия с разметкой XAML.
Файл App.xaml содержит ресурсы XAML на уровне приложения, такие как цвета, стили или шаблоны. Файл App.xaml.cs обычно содержит код, который создает экземпляр приложения Оболочки. В этом проекте он указывает на
AppShell
класс.AppShell.xaml и AppShell.xaml.cs
Этот файл определяет
AppShell
класс, который используется для определения визуальной иерархии приложения.MainPage.xaml и MainPage.xaml.cs
Это страница запуска, отображаемая приложением. Файл MainPage.xaml определяет пользовательский интерфейс (пользовательский интерфейс) страницы. MainPage.xaml.cs содержит код для XAML, например код события нажатия кнопки.
Добавление страницы about
Первая настройка, которую вы сделаете, — добавление еще одной страницы в проект. Эта страница представляет собой страницу about, которая представляет сведения об этом приложении, например автор, версия и, возможно, ссылку для получения дополнительных сведений.
В области Обозреватель решений Visual Studio щелкните правой кнопкой мыши проект> "Заметки" "Добавить>новый элемент...".
В диалоговом окне "Добавление нового элемента" выберите .NET MAUI в списке шаблонов в левой части окна. Затем выберите шаблон .NET MAUI ContentPage (XAML). Присвойте файлу AboutPage.xaml и нажмите кнопку "Добавить".
Файл AboutPage.xaml откроет новую вкладку документа, отображающую всю разметку XAML, представляющую пользовательский интерфейс страницы. Замените разметку XAML следующим разметкой:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.AboutPage"> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="Notes" VerticalOptions="End" /> <Label FontSize="22" Text="v1.0" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="This app is written in XAML and C# with .NET MAUI." /> <Button Text="Learn more..." Clicked="LearnMore_Clicked" /> </VerticalStackLayout> </ContentPage>
Сохраните файл, нажав клавиши CTRL+S или выбрав меню "Сохранить>aboutPage.xaml".
Давайте разберем ключевые части элементов управления XAML, размещенные на странице:
<ContentPage>
— корневой объект дляAboutPage
класса.<VerticalStackLayout>
является единственным дочерним объектом ContentPageобъекта . ContentPage может иметь только один дочерний объект. Тип VerticalStackLayout может иметь несколько дочерних элементов. Этот элемент управления макета упорядочивает дочерние элементы по вертикали, один после другого.<HorizontalStackLayout>
работает так же, как и<VerticalStackLayout>
в случае с дочерними элементами, расположенными по горизонтали.<Image>
отображает изображение, в этом случае используется образ, который поставляется с каждым проектомdotnet_bot.png
MAUI .NET.Внимание
Файл, добавленный в проект, фактически
dotnet_bot.svg
. .NET MAUI преобразует масштабируемые векторные графические файлы (SVG) в переносимые сетевые графические файлы (PNG) на основе целевого устройства. Поэтому при добавлении SVG-файла в проект приложения .NET MAUI следует ссылаться из XAML или C# с расширением.png
. Единственная ссылка на SVG-файл должна находиться в файле проекта.<Label>
элементы управления отображают текст.<Button>
элементы управления могут быть нажаты пользователем, которые вызываютClicked
событие. Вы можете запустить код в ответ наClicked
событие.Clicked="LearnMore_Clicked"
Событие
Clicked
кнопки назначаетсяLearnMore_Clicked
обработчику событий, который будет определен в файле кода программной части. Этот код будет создан на следующем шаге.
Обработка события clicked
Следующим шагом является добавление кода для события кнопки Clicked
.
В области Обозреватель решений Visual Studio разверните файл AboutPage.xaml, чтобы отобразить AboutPage.xaml.cs файла кода. Затем дважды щелкните файл AboutPage.xaml.cs , чтобы открыть его в редакторе кода.
Добавьте следующий
LearnMore_Clicked
код обработчика событий, который открывает системный браузер для определенного URL-адреса:private async void LearnMore_Clicked(object sender, EventArgs e) { // Navigate to the specified URL in the system browser. await Launcher.Default.OpenAsync("https://aka.ms/maui"); }
Обратите внимание, что
async
ключевое слово было добавлено в объявление метода, которое позволяет использоватьawait
ключевое слово при открытии системного браузера.Сохраните файл, нажав клавиши CTRL+S или выбрав меню "Сохранить файл>" AboutPage.xaml.cs.
Теперь, когда xaml и код отложены AboutPage
, вам потребуется отобразить его в приложении.
Добавление ресурсов изображения
Некоторые элементы управления могут использовать изображения, что улучшает взаимодействие пользователей с приложением. В этом разделе вы скачайте два образа, которые будут использоваться в приложении, а также два альтернативных образа для использования с iOS.
Скачайте следующие образы:
Значок: О
Это изображение используется в качестве значка для созданной ранее страницы.Значок: Заметки
Это изображение используется в качестве значка для страницы заметок, которую вы создадите в следующей части этого руководства.
После скачивания изображений их можно переместить с помощью проводник в папку Resources\Images проекта. Любой файл в этой папке автоматически входит в проект как ресурс MauiImage . Вы также можете использовать Visual Studio для добавления изображений в проект. При перемещении изображений вручную пропустите следующую процедуру.
Внимание
Не пропускайте скачивание образов, относящихся к iOS, они необходимы для выполнения этого руководства.
Перемещение изображений с помощью Visual Studio
В области Обозреватель решений Visual Studio разверните папку "Ресурсы", которая отображает папку "Изображения".
Совет
Вы можете использовать проводник для перетаскивания изображений непосредственно в область Обозреватель решений в верхней части папки "Изображения". Это автоматически перемещает файлы в папку и включает их в проект. Если вы решили перетащить файлы, пропустить остальную часть этой процедуры.
Щелкните правой кнопкой мыши изображения и выберите "Добавить>существующий элемент...".
Перейдите в папку, содержащую скачанные изображения.
Измените фильтр на фильтр типа файла на "Файлы изображений".
Удерживайте клавишу CTRL и щелкните все загруженные изображения, а затем нажмите кнопку "Добавить".
Изменение оболочки приложения
Как отмечалось в начале этой статьи, AppShell
класс определяет визуальную иерархию приложения, разметку XAML, используемую при создании пользовательского интерфейса приложения. Обновите XAML, чтобы добавить TabBar элемент управления:
Дважды щелкните файл AppShell.xaml в области Обозреватель решений, чтобы открыть редактор XAML. Замените разметку XAML следующим кодом:
<?xml version="1.0" encoding="UTF-8" ?> <Shell x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Notes" Shell.FlyoutBehavior="Disabled"> <TabBar> <ShellContent Title="Notes" ContentTemplate="{DataTemplate local:MainPage}" Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" /> <ShellContent Title="About" ContentTemplate="{DataTemplate local:AboutPage}" Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" /> </TabBar> </Shell>
Сохраните файл, нажав клавиши CTRL+S или выбрав меню "Сохранить appShell.xaml>".
Давайте разберем ключевые части XAML:
<Shell>
является корневым объектом разметки XAML.<TabBar>
— содержимое Shellобъекта .- Два
<ShellContent>
объекта внутри<TabBar>
объекта . Перед заменой кода шаблона существовал один<ShellContent>
объект, указывающий на страницуMainPage
.
Дочерние TabBar
элементы не представляют никаких элементов пользовательского интерфейса, а скорее организацию визуальной иерархии приложения. Оболочка принимает эти объекты и создает пользовательский интерфейс для содержимого с панелью в верхней части, представляющей каждую страницу. Свойство для каждой ShellContent.Icon
страницы использует специальный синтаксис: {OnPlatform ...}
Этот синтаксис обрабатывается при компиляции страниц XAML для каждой платформы и с его помощью можно указать значение свойства для каждой платформы. В этом случае каждая платформа использует icon_about.png
значок по умолчанию, но будет использоваться icon_about_ios.png
iOS и MacCatalyst.
Каждый <ShellContent>
объект указывает на страницу для отображения. Это свойство задается свойством ContentTemplate
.
Выполнить приложение
Запустите приложение, нажав клавишу F5 или нажав кнопку воспроизведения в верхней части Visual Studio:
Вы увидите, что есть две вкладки: Заметки и сведения. Нажмите вкладку "О программе" , а приложение переходит к созданному AboutPage
файлу. Нажмите кнопку Learn More... , чтобы открыть веб-браузер.
Закройте приложение и вернитесь в Visual Studio. Если вы используете эмулятор Android, завершите работу приложения на виртуальном устройстве или нажмите кнопку остановки в верхней части Visual Studio:
Создание страницы для заметки
Теперь, когда приложение содержит MainPage
и AboutPage
, вы можете приступить к созданию остальной части приложения. Сначала вы создадите страницу, которая позволяет пользователю создавать и отображать заметки, а затем писать код для загрузки и сохранения заметки.
Страница заметок отобразит заметку и позволит сохранить или удалить ее. Сначала добавьте новую страницу в проект:
В области Обозреватель решений Visual Studio щелкните правой кнопкой мыши проект> "Заметки" "Добавить>новый элемент...".
В диалоговом окне "Добавление нового элемента" выберите .NET MAUI в списке шаблонов в левой части окна. Затем выберите шаблон .NET MAUI ContentPage (XAML). Присвойте файлу NotePage.xaml и нажмите кнопку "Добавить".
Файл NotePage.xaml откроется на новой вкладке, отображая всю разметку XAML, представляющую пользовательский интерфейс страницы. Замените разметку кода XAML следующей разметкой:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.NotePage" Title="Note"> <VerticalStackLayout Spacing="10" Margin="5"> <Editor x:Name="TextEditor" Placeholder="Enter your note" HeightRequest="100" /> <Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Button Text="Save" Clicked="SaveButton_Clicked" /> <Button Grid.Column="1" Text="Delete" Clicked="DeleteButton_Clicked" /> </Grid> </VerticalStackLayout> </ContentPage>
Сохраните файл, нажав клавиши CTRL+S или выбрав меню "Сохранить>примечаниеPage.xaml".
Давайте разберем ключевые части элементов управления XAML, размещенные на странице:
<VerticalStackLayout>
упорядочивает дочерние элементы управления по вертикали, один ниже другого.<Editor>
— это элемент управления многострочного текстового VerticalStackLayoutредактора, который является первым элементом управления внутри .<Grid>
— это элемент управления макетом и второй элемент управления внутри VerticalStackLayout.Этот элемент управления определяет столбцы и строки для создания ячеек. Дочерние элементы управления помещаются в эти ячейки.
По умолчанию элемент Grid управления содержит одну строку и столбец, создав одну ячейку. Столбцы определяются шириной, а
*
значение ширины указывает столбцу, чтобы заполнить максимально возможное пространство. Предыдущий фрагмент кода определил два столбца, как можно больше места, что равномерно распределяет столбцы в выделенном пространстве:ColumnDefinitions="*,*"
Размеры столбцов разделены символом,
.Столбцы и строки, определенные индексом Grid , начинаются с 0. Таким образом, первый столбец будет индексировать 0, второй столбец — индекс 1 и т. д.
Два
<Button>
элемента управления находятся внутри<Grid>
и назначены столбцу. Если дочерний элемент управления не определяет назначение столбца, он автоматически назначается первому столбцу. В этой разметке первая кнопка — кнопка "Сохранить" и автоматически назначена первому столбцу, столбцу 0. Второй кнопкой является кнопка "Удалить" и назначена второму столбцу, столбцу 1.Обратите внимание, что две кнопки обрабатывают
Clicked
событие. Вы добавите код для этих обработчиков в следующем разделе.
Загрузка и сохранение заметки
Откройте файл NotePage.xaml.cs кода программной части. Код можно открыть для файла NotePage.xaml тремя способами:
- Если файл NotePage.xaml открыт и является активным документом, редактируемым, нажмите клавишу F7.
- Если файл NotePage.xaml открыт и является активным документом, щелкните правой кнопкой мыши в текстовом редакторе и выберите "Просмотреть код".
- Используйте Обозреватель решений, чтобы развернуть запись NotePage.xaml, показывающую файл NotePage.xaml.cs. Дважды щелкните файл, чтобы открыть его.
При добавлении нового XAML-файла код программной части содержит одну строку в конструкторе, вызов InitializeComponent
метода:
namespace Notes;
public partial class NotePage : ContentPage
{
public NotePage()
{
InitializeComponent();
}
}
Метод InitializeComponent
считывает разметку XAML и инициализирует все объекты, определенные разметкой. Объекты подключены в их отношениях с родительским дочерним элементом, а обработчики событий, определенные в коде, присоединяются к событиям, заданным в XAML.
Теперь, когда вы узнаете больше о файлах программной части, вы добавите код в файл NotePage.xaml.cs программной части для обработки загрузки и сохранения заметок.
При создании заметки оно сохраняется на устройстве в виде текстового файла. Имя файла представлено переменной
_fileName
. Добавьте в класс следующееstring
объявление переменнойNotePage
:public partial class NotePage : ContentPage { string _fileName = Path.Combine(FileSystem.AppDataDirectory, "notes.txt");
Приведенный выше код создает путь к файлу, сохраняя его в локальном каталоге данных приложения. Имя файла notes.txt.
В конструкторе класса после
InitializeComponent
вызова метода прочитайте файл с устройства и сохраните его содержимое вTextEditor
свойстве элемента управленияText
:public NotePage() { InitializeComponent(); if (File.Exists(_fileName)) TextEditor.Text = File.ReadAllText(_fileName); }
Затем добавьте код для обработки событий, определенных
Clicked
в XAML:private void SaveButton_Clicked(object sender, EventArgs e) { // Save the file. File.WriteAllText(_fileName, TextEditor.Text); } private void DeleteButton_Clicked(object sender, EventArgs e) { // Delete the file. if (File.Exists(_fileName)) File.Delete(_fileName); TextEditor.Text = string.Empty; }
Метод
SaveButton_Clicked
записывает текст в Editor элемент управления в файл, представленный переменной_fileName
.Метод
DeleteButton_Clicked
сначала проверяет, является ли файл, представленный переменной_fileName
, и если он существует, удаляет его. Editor Затем текст элемента управления очищается.Сохраните файл, нажав клавиши CTRL+S или выбрав меню "Сохранить файл>" NotePage.xaml.cs.
Окончательный код файла программной части должен выглядеть следующим образом:
namespace Notes;
public partial class NotePage : ContentPage
{
string _fileName = Path.Combine(FileSystem.AppDataDirectory, "notes.txt");
public NotePage()
{
InitializeComponent();
if (File.Exists(_fileName))
TextEditor.Text = File.ReadAllText(_fileName);
}
private void SaveButton_Clicked(object sender, EventArgs e)
{
// Save the file.
File.WriteAllText(_fileName, TextEditor.Text);
}
private void DeleteButton_Clicked(object sender, EventArgs e)
{
// Delete the file.
if (File.Exists(_fileName))
File.Delete(_fileName);
TextEditor.Text = string.Empty;
}
}
Проверка заметки
Теперь, когда страница примечания завершена, вам потребуется способ представить его пользователю. Откройте файл AppShell.xaml и измените первую ShellContent запись, чтобы указать на нее NotePage
MainPage
:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate local:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate local:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Сохраните файл и запустите приложение. Попробуйте ввести поле записи и нажмите кнопку "Сохранить ". Закройте приложение и снова откройте его. Введенное примечание должно быть загружено из хранилища устройства.
Привязка данных к пользовательскому интерфейсу и страницам навигации
В этой части руководства представлены основные понятия представлений, моделей и навигации в приложении.
В предыдущих шагах руководства вы добавили две страницы в проект: NotePage
и AboutPage
. Страницы представляют представление данных. Это NotePage
представление, отображающее "данные заметок" и AboutPage
"представление", отображающее "данные сведений о приложении". Оба этих представления имеют модель жестко закодированных данных или внедренных в них данных, и вам потребуется разделить модель данных от представления.
Что такое преимущество разделения модели от представления? Это позволяет создавать представление для представления и взаимодействия с любой частью модели, не беспокоясь о фактическом коде, реализуемом моделью. Это достигается с помощью привязки данных, которая будет представлена далее в этом руководстве. Сейчас, однако, позволяет реструктурировать проект.
Разделение представления и модели
Рефакторинг существующего кода для разделения модели с представлением. Следующие несколько шагов упорядочит код таким образом, чтобы представления и модели были определены отдельно друг от друга.
Удалите MainPage.xaml и MainPage.xaml.cs из проекта, они больше не нужны. В области Обозреватель решений найдите запись для MainPage.xaml, щелкните ее правой кнопкой мыши и выберите "Удалить".
Совет
Удаление элемента MainPage.xaml также должно удалить элемент MainPage.xaml.cs . Если MainPage.xaml.cs не удалены, щелкните его правой кнопкой мыши и нажмите кнопку " Удалить".
Щелкните проект правой Notes кнопкой мыши и выберите команду "Добавить>новую папку". Назовите папку Models.
Щелкните проект правой Notes кнопкой мыши и выберите команду "Добавить>новую папку". Назовите папку Views.
Найдите элемент NotePage.xaml и перетащите его в папкуViews. NotePage.xaml.cs должен перемещаться вместе с ним.
Внимание
При перемещении файла Visual Studio обычно запрашивает предупреждение о том, как операция перемещения может занять много времени. Это не должно быть проблемой здесь, нажмите кнопку ОК , если вы видите это предупреждение.
Visual Studio также может попросить вас настроить пространство имен перемещаемого файла. Выберите "Нет" , так как следующие шаги изменят пространство имен.
Найдите элемент AboutPage.xaml и перетащите его в папкуViews. AboutPage.xaml.cs должен перемещаться с ним.
Обновление пространства имен представления
Теперь, когда представления были перемещены Views в папку, необходимо обновить пространства имен для сопоставления. Пространство имен для файлов XAML и кода для страниц имеет значение Notes
. Это необходимо обновить до Notes.Views
.
В области Обозреватель решений разверните файл NotePage.xaml и AboutPage.xaml, чтобы отобразить файлы кода программной части:
Дважды щелкните элемент NotePage.xaml.cs , чтобы открыть редактор кода. Измените пространство
Notes.Views
имен на:namespace Notes.Views;
Повторите предыдущие шаги для элемента AboutPage.xaml.cs .
Дважды щелкните элемент NotePage.xaml , чтобы открыть редактор XAML. Старое пространство имен ссылается через
x:Class
атрибут, который определяет тип класса, который является кодом для XAML. Эта запись не только пространство имен, но пространство имен с типом. Измените значениеNotes.Views.NotePage
наx:Class
:x:Class="Notes.Views.NotePage"
Повторите предыдущий шаг для элемента AboutPage.xaml , но присвойте этому значению
x:Class
значениеNotes.Views.AboutPage
.
Исправлена ссылка на пространство имен в Оболочке
AppShell.xaml определяет две вкладки, одну для NotesPage
другой.AboutPage
Теперь, когда эти две страницы были перемещены в новое пространство имен, сопоставление типов в XAML теперь недопустимо. В области Обозреватель решений дважды щелкните запись AppShell.xaml, чтобы открыть ее в редакторе XAML. Он должен выглядеть следующим фрагментом кода:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Notes"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate local:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate local:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Пространство имен .NET импортируется в XAML через объявление пространства имен XML. В предыдущей разметке XAML это xmlns:local="clr-namespace:Notes"
атрибут в корневом элементе: <Shell>
Формат объявления пространства имен XML для импорта пространства имен .NET в той же сборке:
xmlns:{XML namespace name}="clr-namespace:{.NET namespace}"
Поэтому предыдущее объявление сопоставляет пространство local
имен XML с пространством Notes
имен .NET. Обычно рекомендуется сопоставить имя local
с корневым пространством имен проекта.
local
Удалите пространство имен XML и добавьте новое. Это новое пространство имен XML будет сопоставляться с пространством Notes.Views
имен .NET, поэтому оно называется views
. Объявление должно выглядеть следующим образом: xmlns:views="clr-namespace:Notes.Views"
Пространство local
имен XML использовалось свойствами ShellContent.ContentTemplate
, измените их на views
. Теперь xaml должен выглядеть следующим фрагментом кода:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Notes.Views"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate views:NotePage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate views:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Теперь вы должны иметь возможность запускать приложение без каких-либо ошибок компилятора, и все должно работать как раньше.
Определение модели
В настоящее время модель — это данные, внедренные в заметку и о представлениях. Мы создадим новые классы для представления данных. Во-первых, модель, представляющая данные страницы заметок:
В области Обозреватель решений щелкните папку правой кнопкой мыши Models и выберите "Добавить>класс...".
Назовите класс Note.cs и нажмите клавишу Add.
Откройте Note.cs и замените код следующим фрагментом кода:
namespace Notes.Models; internal class Note { public string Filename { get; set; } public string Text { get; set; } public DateTime Date { get; set; } }
Сохраните файл.
Затем создайте модель страницы:
В области Обозреватель решений щелкните папку правой кнопкой мыши Models и выберите "Добавить>класс...".
Назовите класс About.cs и нажмите клавишу Add.
Откройте About.cs и замените код следующим фрагментом кода:
namespace Notes.Models; internal class About { public string Title => AppInfo.Name; public string Version => AppInfo.VersionString; public string MoreInfoUrl => "https://aka.ms/maui"; public string Message => "This app is written in XAML and C# with .NET MAUI."; }
Сохраните файл.
Обновление страницы "О программе"
Страница о странице будет самой быстрой для обновления, и вы сможете запустить приложение и узнать, как он загружает данные из модели.
В области Обозреватель решений откройте файл Views\AboutPage.xaml.
Замените содержимое следующим фрагментом кода:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:models="clr-namespace:Notes.Models" x:Class="Notes.Views.AboutPage"> <ContentPage.BindingContext> <models:About /> </ContentPage.BindingContext> <VerticalStackLayout Spacing="10" Margin="10"> <HorizontalStackLayout Spacing="10"> <Image Source="dotnet_bot.png" SemanticProperties.Description="The dot net bot waving hello!" HeightRequest="64" /> <Label FontSize="22" FontAttributes="Bold" Text="{Binding Title}" VerticalOptions="End" /> <Label FontSize="22" Text="{Binding Version}" VerticalOptions="End" /> </HorizontalStackLayout> <Label Text="{Binding Message}" /> <Button Text="Learn more..." Clicked="LearnMore_Clicked" /> </VerticalStackLayout> </ContentPage>
Рассмотрим измененные строки, которые выделены в предыдущем фрагменте кода:
xmlns:models="clr-namespace:Notes.Models"
Эта строка сопоставляет
Notes.Models
пространство имен .NET с пространствомmodels
имен XML.Свойство
BindingContext
ContentPage этого класса имеет значение экземпляраNote.Models.About
класса, используя пространство имен XML и объектmodels:About
. Это было задано с помощью синтаксиса элемента свойства вместо атрибута XML.Внимание
До сих пор свойства заданы с помощью XML-атрибута. Это отлично подходит для простых значений
Label.FontSize
, таких как свойство. Но если значение свойства является более сложным, необходимо использовать синтаксис элемента свойства для создания объекта. Рассмотрим следующий пример создания метки со своимFontSize
набором свойств:<Label FontSize="22" />
То же
FontSize
свойство можно задать с помощью синтаксиса элемента свойства:<Label> <Label.FontSize> 22 </Label.FontSize> </Label>
Три
<Label>
элемента управления изменилиText
значение свойства с жестко закодированных строк на синтаксис привязки:{Binding PATH}
{Binding}
синтаксис обрабатывается во время выполнения, что позволяет возвращать значение из привязки динамическим.{Binding PATH}
ЧастьюPATH
является путь свойства для привязки. Свойство исходит из текущего элемента управленияBindingContext
. При использовании элемента управленияBindingContext
не<Label>
задано. Контекст наследуется от родительского элемента управления, если он не настроен элементом управления, который в данном случае контекст параметра родительского объекта является корневым объектом: ContentPageОбъект в объекте
BindingContext
является экземпляромAbout
модели. Путь привязки одной из меток привязываетLabel.Text
свойство к свойствуAbout.Title
.
Последнее изменение страницы обновляет кнопку, открывающую веб-страницу. URL-адрес был жестко закодирован в коде программной части, но URL-адрес должен поступать из модели, которая находится в свойстве BindingContext
.
В области Обозреватель решений откройте файл Views\AboutPage.xaml.cs.
Замените метод
LearnMore_Clicked
следующим кодом:private async void LearnMore_Clicked(object sender, EventArgs e) { if (BindingContext is Models.About about) { // Navigate to the specified URL in the system browser. await Launcher.Default.OpenAsync(about.MoreInfoUrl); } }
При просмотре выделенной строки код проверяет, является ли BindingContext
тип типом Models.About
, а если он есть, назначает его переменной about
. Следующая строка внутри инструкции if
открывает браузер к URL-адресу, предоставленному свойством about.MoreInfoUrl
.
Запустите приложение, и вы увидите, что он выполняется точно так же, как и раньше. Попробуйте изменить значения модели и узнать, как пользовательский интерфейс и URL-адрес, открытый браузером, также изменится.
Страница обновления заметок
Предыдущий раздел привязывает about представление страницы к about модели, и теперь вы сделаете то же самое, привязав note представление к note модели. Однако в этом случае модель не будет создана в XAML, но будет предоставлена в коде в следующих нескольких шагах.
В области Обозреватель решений откройте файл Views\NotePage.xaml.
Измените
<Editor>
элемент управления, добавивText
свойство. Привязка свойства к свойствуText
: :<Editor ... Text="{Binding Text}"
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.NotePage" Title="Note"> <VerticalStackLayout Spacing="10" Margin="5"> <Editor x:Name="TextEditor" Placeholder="Enter your note" Text="{Binding Text}" HeightRequest="100" /> <Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Button Text="Save" Clicked="SaveButton_Clicked" /> <Button Grid.Column="1" Text="Delete" Clicked="DeleteButton_Clicked" /> </Grid> </VerticalStackLayout> </ContentPage>
Изменения кода в коде сложнее, чем XAML. Текущий код загружает содержимое файла в конструкторе, а затем задает его непосредственно свойству TextEditor.Text
. Вот как выглядит текущий код:
public NotePage()
{
InitializeComponent();
if (File.Exists(_fileName))
TextEditor.Text = File.ReadAllText(_fileName);
}
Вместо загрузки заметки в конструкторе создайте новый LoadNote
метод. Этот метод выполняет следующие действия:
- Примите параметр имени файла.
- Создайте модель заметки и задайте имя файла.
- Если файл существует, загрузите его содержимое в модель.
- Если файл существует, обновите модель с датой создания файла.
BindingContext
Задайте для модели страницу.
В области Обозреватель решений откройте файл Views\NotePage.xaml.cs.
Добавьте приведенный ниже метод в класс :
private void LoadNote(string fileName) { Models.Note noteModel = new Models.Note(); noteModel.Filename = fileName; if (File.Exists(fileName)) { noteModel.Date = File.GetCreationTime(fileName); noteModel.Text = File.ReadAllText(fileName); } BindingContext = noteModel; }
Обновите конструктор класса для вызова
LoadNote
. Имя файла для заметки должно быть случайным образом сформированным именем, которое будет создано в локальном каталоге данных приложения.public NotePage() { InitializeComponent(); string appDataPath = FileSystem.AppDataDirectory; string randomFileName = $"{Path.GetRandomFileName()}.notes.txt"; LoadNote(Path.Combine(appDataPath, randomFileName)); }
Добавление представления и модели, в которую перечислены все заметки
Эта часть руководства добавляет окончательный фрагмент приложения, представление, отображающее все созданные ранее заметки.
Несколько заметок и навигации
В настоящее время в представлении заметок отображается одна заметка. Чтобы отобразить несколько заметок, создайте новое представление и модель: AllNotes.
- В области Обозреватель решений щелкните правой кнопкой мыши Views папку и выберите "Добавить>новый элемент".
- В диалоговом окне "Добавление нового элемента" выберите .NET MAUI в списке шаблонов в левой части окна. Затем выберите шаблон .NET MAUI ContentPage (XAML). Присвойте файлу AllNotesPage.xaml и нажмите кнопку "Добавить".
- В области Обозреватель решений щелкните правой кнопкой мыши Models папку и выберите "Добавить>класс...
- Назовите класс AllNotes.cs и нажмите клавишу Add.
Код модели AllNotes
Новая модель будет представлять данные, необходимые для отображения нескольких заметок. Эти данные будут свойством, представляющим коллекцию заметок. Коллекция будет ObservableCollection
специализированной коллекцией. Если элемент управления, который перечисляет несколько элементов, например ListView, привязан к объекту ObservableCollection
, они работают вместе, чтобы автоматически сохранить список элементов в синхронизации с коллекцией. Если список добавляет элемент, коллекция обновляется. Если коллекция добавляет элемент, элемент управления автоматически обновляется новым элементом.
В области Обозреватель решений откройте файл Models\AllNotes.cs.
Замените код следующим фрагментом кода:
using System.Collections.ObjectModel; namespace Notes.Models; internal class AllNotes { public ObservableCollection<Note> Notes { get; set; } = new ObservableCollection<Note>(); public AllNotes() => LoadNotes(); public void LoadNotes() { Notes.Clear(); // Get the folder where the notes are stored. string appDataPath = FileSystem.AppDataDirectory; // Use Linq extensions to load the *.notes.txt files. IEnumerable<Note> notes = Directory // Select the file names from the directory .EnumerateFiles(appDataPath, "*.notes.txt") // Each file name is used to create a new Note .Select(filename => new Note() { Filename = filename, Text = File.ReadAllText(filename), Date = File.GetLastWriteTime(filename) }) // With the final collection of notes, order them by date .OrderBy(note => note.Date); // Add each note into the ObservableCollection foreach (Note note in notes) Notes.Add(note); } }
Предыдущий код объявляет коллекцию, именованную Notes
и использует LoadNotes
метод для загрузки заметок с устройства. Этот метод использует расширения LINQ для загрузки, преобразования и сортировки данных в коллекцию Notes
.
Проектирование страницы AllNotes
Затем представление должно быть разработано для поддержки модели AllNotes .
В области Обозреватель решений откройте файл Views\AllNotesPage.xaml.
Замените код следующим разметкой:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Notes.Views.AllNotesPage" Title="Your Notes"> <!-- Add an item to the toolbar --> <ContentPage.ToolbarItems> <ToolbarItem Text="Add" Clicked="Add_Clicked" IconImageSource="{FontImage Glyph='+', Color=Black, Size=22}" /> </ContentPage.ToolbarItems> <!-- Display notes in a list --> <CollectionView x:Name="notesCollection" ItemsSource="{Binding Notes}" Margin="20" SelectionMode="Single" SelectionChanged="notesCollection_SelectionChanged"> <!-- Designate how the collection of items are laid out --> <CollectionView.ItemsLayout> <LinearItemsLayout Orientation="Vertical" ItemSpacing="10" /> </CollectionView.ItemsLayout> <!-- Define the appearance of each item in the list --> <CollectionView.ItemTemplate> <DataTemplate> <StackLayout> <Label Text="{Binding Text}" FontSize="22"/> <Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage>
В предыдущем XAML представлены несколько новых концепций:
Свойство
ContentPage.ToolbarItems
содержит объектToolbarItem
. Кнопки, определенные здесь, обычно отображаются в верхней части приложения вдоль заголовка страницы. В зависимости от платформы, однако, она может находиться в другой позиции. При нажатииClicked
одной из этих кнопок событие вызывается так же, как и обычная кнопка.Свойство
ToolbarItem.IconImageSource
задает значок для отображения на кнопке. В этом примереFontImage
используется любой ресурс изображения, определенный проектом. МожноFontImage
использовать один глиф из шрифта в качестве изображения.Элемент CollectionView управления отображает коллекцию элементов, и в данном случае привязан к свойству модели
Notes
. Способ представления каждого элемента представлением коллекции определяется с помощьюCollectionView.ItemsLayout
свойств иCollectionView.ItemTemplate
свойств.Для каждого элемента в коллекции
CollectionView.ItemTemplate
создается объявленный XAML. ЭтотBindingContext
XAML становится элементом коллекции, в данном случае, каждое отдельное примечание. Шаблон заметки использует две метки, которые привязаны к заметкеText
иDate
свойствам.Обрабатывает CollectionView событие, которое возникает
SelectionChanged
при выборе элемента в представлении коллекции.
Код для представления должен быть записан для загрузки заметок и обработки событий.
В области Обозреватель решений откройте файл Views/AllNotesPage.xaml.cs.
Замените код следующим фрагментом кода:
namespace Notes.Views; public partial class AllNotesPage : ContentPage { public AllNotesPage() { InitializeComponent(); BindingContext = new Models.AllNotes(); } protected override void OnAppearing() { ((Models.AllNotes)BindingContext).LoadNotes(); } private async void Add_Clicked(object sender, EventArgs e) { await Shell.Current.GoToAsync(nameof(NotePage)); } private async void notesCollection_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.CurrentSelection.Count != 0) { // Get the note model var note = (Models.Note)e.CurrentSelection[0]; // Should navigate to "NotePage?ItemId=path\on\device\XYZ.notes.txt" await Shell.Current.GoToAsync($"{nameof(NotePage)}?{nameof(NotePage.ItemId)}={note.Filename}"); // Unselect the UI notesCollection.SelectedItem = null; } } }
Этот код использует конструктор для задания BindingContext
страницы модели.
Метод OnAppearing
переопределяется из базового класса. Этот метод автоматически вызывается при отображении страницы, например при переходе на страницу. Приведенный здесь код указывает модели загружать заметки. Так как в CollectionView представлении AllNotes привязано к свойству модели Notes
AllNotes, которое является ObservableCollection
автоматическим CollectionView обновлением при загрузке заметок.
Обработчик Add_Clicked
представляет другую новую концепцию навигации. Так как приложение использует оболочку .NET MAUI, вы можете перейти на страницы, вызвав Shell.Current.GoToAsync
метод. Обратите внимание, что обработчик объявлен с async
ключевым словом, который позволяет использовать await
ключевое слово при переходе. Этот обработчик переходит к элементу NotePage
.
Последний фрагмент кода в предыдущем фрагменте кода — notesCollection_SelectionChanged
обработчик. Этот метод принимает выбранный в данный момент элемент, Note модель и использует ее сведения для перехода к элементу NotePage
. GoToAsync использует строку URI для навигации. В этом случае строка создается, использующая параметр строки запроса для задания свойства на целевой странице. Интерполированная строка, представляющая универсальный код ресурса (URI), выглядит примерно так:
NotePage?ItemId=path\on\device\XYZ.notes.txt
Параметр ItemId=
имеет имя файла на устройстве, где хранится заметка.
Visual Studio может указывать на то, что NotePage.ItemId
свойство не существует, которое он не существует. Следующий шаг — изменение Note представления для загрузки модели на основе создаваемого ItemId
параметра.
Параметры строки запроса
Представление Note должно поддерживать параметр строки запроса. ItemId
Создайте его сейчас:
В области Обозреватель решений откройте файл Views/NotePage.xaml.cs.
Добавьте атрибут в
QueryProperty
class
ключевое слово, указав имя свойства строки запроса и свойство класса, с которое оно сопоставляется,ItemId
иItemId
соответственно:[QueryProperty(nameof(ItemId), nameof(ItemId))] public partial class NotePage : ContentPage
Добавьте новое
string
свойство с именемItemId
. Это свойство вызываетLoadNote
метод, передав значение свойства, которое, в свою очередь, должно быть именем файла заметки:public string ItemId { set { LoadNote(value); } }
Замените
SaveButton_Clicked
DeleteButton_Clicked
обработчики следующим кодом:private async void SaveButton_Clicked(object sender, EventArgs e) { if (BindingContext is Models.Note note) File.WriteAllText(note.Filename, TextEditor.Text); await Shell.Current.GoToAsync(".."); } private async void DeleteButton_Clicked(object sender, EventArgs e) { if (BindingContext is Models.Note note) { // Delete the file. if (File.Exists(note.Filename)) File.Delete(note.Filename); } await Shell.Current.GoToAsync(".."); }
Теперь кнопки
async
. После нажатия кнопки страница возвращается на предыдущую страницу с помощью URI..
._fileName
Удалите переменную из верхней части кода, так как она больше не используется классом.
Изменение визуального дерева приложения
Она AppShell
по-прежнему загружает одну страницу заметок, вместо этого она должна загрузить представление AllPages. Откройте файл AppShell.xaml и измените первую ShellContent запись, чтобы она указывала на нее AllNotesPage
NotePage
:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="Notes.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Notes.Views"
Shell.FlyoutBehavior="Disabled">
<TabBar>
<ShellContent
Title="Notes"
ContentTemplate="{DataTemplate views:AllNotesPage}"
Icon="{OnPlatform 'icon_notes.png', iOS='icon_notes_ios.png', MacCatalyst='icon_notes_ios.png'}" />
<ShellContent
Title="About"
ContentTemplate="{DataTemplate views:AboutPage}"
Icon="{OnPlatform 'icon_about.png', iOS='icon_about_ios.png', MacCatalyst='icon_about_ios.png'}" />
</TabBar>
</Shell>
Если вы запускаете приложение сейчас, вы заметите, что он завершается сбоем, если вы нажимаете кнопку "Добавить", жалуясь на то, что он не может перейти.NotesPage
Каждую страницу, которую можно перейти с другой страницы, необходимо зарегистрировать в системе навигации. Страницы AllNotesPage
автоматически регистрируются в системе навигации, объявляясь в этой системеTabBar.AboutPage
Зарегистрируйте систему NotesPage
навигации:
В области Обозреватель решений откройте файл AppShell.xaml.cs.
Добавьте строку в конструктор, который регистрирует маршрут навигации:
namespace Notes; public partial class AppShell : Shell { public AppShell() { InitializeComponent(); Routing.RegisterRoute(nameof(Views.NotePage), typeof(Views.NotePage)); } }
Метод Routing.RegisterRoute
принимает два параметра:
- Первым параметром является строковое имя универсального кода ресурса (URI), который требуется зарегистрировать, в этом случае разрешенное имя —
"NotePage"
это. - Второй параметр — это тип страницы для загрузки при
"NotePage"
переходе.
Теперь вы можете запустить приложение. Попробуйте добавить новые заметки, перейти между заметками и удалить их.
Изучите код для этого руководства.. Если вы хотите скачать копию завершенного проекта для сравнения кода с этим проектом.
Поздравляем!
Вы завершили учебник по созданию приложения .NET MAUI!
Следующие шаги
В следующей части серии учебников вы узнаете, как реализовать в проекте шаблоны модели -view-viewmodel (MVVM).
Следующие ссылки содержат дополнительные сведения, связанные с некоторыми понятиями, которые вы узнали в этом руководстве:
Возникла проблема с этим разделом? Если это так, отправьте нам отзыв, чтобы мы исправили этот раздел.