За управление визуальным интерфейсом в приложении Maui отвечает класс, который реализует интерфейс IApplication. В стандартном проекте Maui уже по умолчанию имеется подобная реализация в виде класса App, который определен в файле App.xaml.cs:
namespace HelloApp; public partial class App : Application { public App() { InitializeComponent(); MainPage = new AppShell(); } }
Однако сам класс App непосредственно не определяет графический интерфейс - он лишь устанавливается главную страницу с помощью свойства MainPage.
MainPage = new AppShell();
В .NET MAUI графический интерфейс состоит из страниц. Каждая страница представляет собой объект класса Page. Страница занимает все пространство экрана. То есть то, что мы видим на экране мобильного устройства или в окне десктопного приложения - это страница. Приложение может иметь одну или несколько страниц.
В данном случае в качестве главной страницы приложения используется объект класса AppShell. Одним из родительских классов AppShell является класс Page, поэтому AppShell по сути также является страницей. Однако AppShell не простая страница, а фактически представляет собой оболочку, в которую помещаются простые страницы. Смысл AppShell состоит в том, чтобы упростить организацию страниц и различные действия с ними, в частности, навигацию между страницами.
Определение класса AppShell распределено на два файла: AppShell.xaml и AppShell.xaml.cs.
Непосредственно сам класс AppShell определен в файле AppShell.xaml.cs:
namespace HelloApp { public partial class AppShell : Shell { public AppShell() { InitializeComponent(); } } }
В классе определен только конструктор, который с помощью вызова InitializeComponent()
позволяет загрузить определение интерфейса страницы из файла AppShell.xaml.
Для определения графического интерфейса в .NET MAUI применяется XAML. XAML представляет язык разметки на основе xml для создания объектов декларативным образом (хотя также можно создавать интерфейс в коде C#, либо комбинировать создание интерфейса в коде XAML и C#).
Использование XAML несет некоторые преимущества.
Во-первых, с помощью XAML мы можем отделить графический интерфейс от логики приложения, благодаря чему над разными частями приложения могут относительно автономно работать разные специалисты: над интерфейсом - дизайнеры, над кодом логики - программисты.
Во-вторых, XAML позволяет описать интерфейс более ясным и понятным способом, такой код гораздо проще поддерживать и обновлять.
В целом XAML позволяет организовать весь пользовательский интерфейс в виде набора страниц подобно тому, как это делается в HTML.
Так, обратимся к файлу AppShell.xaml, который определяет интерфейс для страницы AppShell:
<?xml version="1.0" encoding="UTF-8" ?> <Shell x:Class="HelloApp.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:HelloApp" Shell.FlyoutBehavior="Disabled" Title="HelloApp"> <ShellContent Title="Home" ContentTemplate="{DataTemplate local:MainPage}" Route="MainPage" /> </Shell>
Файл с разметкой на xaml представляет собой обычный файл xml, и первой строкой идет стандартное определение xml-файла:
<?xml version="1.0" encoding="UTF-8" ?>
Далее здесь определение корневого элемента, в качестве которого здесь выступает Shell
. XAML предлагает очень простую и ясную схему определения различных элементов и их свойств. Каждый элемент, как и любой элемент XML, должен иметь открытый и закрытый тег, как в случае с элементом Shell:
<Shell></Shell>
Либо элемент может иметь сокращенню форму с закрывающим слешем в конце, наподобие:
<ShellContent />
Каждый элемент в XAML представляет объект определенного класса C#, а атрибуты элементов соотносятся со свойствами этих классом. Например, элемент Shell фактически будет представлять объект одноименного класса Shell (Shell - базовый класс для класса AppShell), а элемент ShellContent - объект класса ShellContent.
Каждый элемент в xaml можно сопоставить определенному классу в C#, а каждый атрибут элемента - свойству этого класса. Например, в у элемента Shell задан атрибут Shell.FlyoutBehavior
:
Shell.FlyoutBehavior="Disabled"
Свойство Shell.FlyoutBehavior настраивает поведение всплывающего сообщения. В качестве значения оно принимает одну из констант перечисления FlyoutBehavior, в данном случае это значение FlyoutBehavior.Disabled.
Хотя в коде xaml атрибуту Shell.FlyoutBehavior передается просто строка "Disabled", но .NET MAUI с помощью конвертеров типов может преобразовать значения атрибутов в нужные типы.
В определении корневого элемента Shell подключаются три пространства имен с помощью атрибутов xmlns.
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:HelloApp"
Пространство имен xmlns="http://schemas.microsoft.com/dotnet/2021/maui" определяет большинство типов из .NET MAUI, которые применяются для построения графического интерфейса.
Второе пространство имен http://schemas.microsoft.com/winfx/2009/xaml определяет ряд типов XAML и типы CLR. Так как только одно
пространство имен может быть базовым, то это пространство используется с префиксом (или проекцируется на префикс) x: xmlns:x
. Это значит, что те свойства элементов, которые заключены в этом пространстве имен,
будут использоваться с префиксом x - x:Name
или x:Class
Третье пространство имен xmlns:local="clr-namespace:HelloApp" позволяет подключить функционал, который определен в текущем проекте (HelloApp). Это
пространство используется с префиксом (или проекцируется на префикс) local: xmlns:local
.
Значение "clr-namespace" указывает на пространство имен CLR внутри сборки, функционал которого надо подключить в XAML. В данном случае это пространство имен HelloApp, которое по умолчанию
соответствует названию проекта. И затем в коде xaml с помощью префикса ocal можно будет обращаться к типам, определенным в текущем проекте.
Также в определении корневого элемента Shell идет атрибут x:Class="HelloApp.AppShell
, который указывает на класс, представляющий данную страницу. То есть данный элемент Shell
будет связан с классом "HelloApp.AppShell. Причем здесь атрибут используется с префиксом x. Это значит, что атрибут Class берется из пространства имен http://schemas.microsoft.com/winfx/2009/xaml, которое проецируется на данный префикс.
Далее внутри Shell определены непосредственно элементы, которые и будут представлять графический интерфейс. В данном случае это один элемент ShellContent:
<ShellContent Title="Home" ContentTemplate="{DataTemplate local:MainPage}" Route="MainPage"/>
Опять же здесь в декларативной форме определяется объект класса ShellContent, который устанавливает содержимое страницы AppShell
Свойство Title
класса ShellContent определяет заголовок страницы, а свойство Route
принимает название корневой страницы.
Свойство ContentTemplate
, которое устанавливает шаблон содержимого страницы, в качестве значения принимает объект DataTemplate. С помощью записи
"{DataTemplate local:MainPage}"
мы указываем, что в качестве шаблона данных будет использоваться страница MainPage,
которая определена в проекте. Префикс local
указывает, что определение класса MainPage будет браться из текущего проекта.
Таким образом, страница AppShell декларативным образом в коде xaml устанавливает заголовок, шаблон страниц и стартовую страницу, в качестве которой выступает класс MainPage.
Собственно визуальный интерфейс и связанная с ним логика в проекте .NET MAUI по умолчанию определяется в классе MainPage, который также разбит на два файла: MainPage.xaml и MainPage.xaml.cs
Сам интрейфейс опять же определяется с помощью кода XAML в файле MainPage.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="HelloApp.MainPage"> <ScrollView> <VerticalStackLayout Padding="30,0" Spacing="25"> <Image Source="dotnet_bot.png" HeightRequest="185" Aspect="AspectFit" SemanticProperties.Description="dot net bot in a race car number eight" /> <Label Text="Hello, World!" Style="{StaticResource Headline}" SemanticProperties.HeadingLevel="Level1" /> <Label Text="Welcome to .NET Multi-platform App UI" Style="{StaticResource SubHeadline}" SemanticProperties.HeadingLevel="Level2" SemanticProperties.Description="Welcome to dot net Multi platform App U I" /> <Button x:Name="CounterBtn" Text="Click me" SemanticProperties.Hint="Counts the number of times you click" Clicked="OnCounterClicked" HorizontalOptions="Fill" /> </VerticalStackLayout> </ScrollView> </ContentPage>
Здесь опять же мы видим ту же самую структуру, что и в случае с файлом AppShell.xaml, только в качестве корневого элемента используется класс ContentPage (базовый класс для MainPage) - страница, которая имеет некоторое содержимое.
В корневом элементе ContentPage также подключаются используемые пространства имен xaml и определяется имя класса с помощью атрибута x:Class.
Внутри ContentPage определяется более сложная структура. Она содержит элемент ScrollView, который определяет прокручиваемую область. Внутри этой области для вертикального расположения элементов применяется класс VerticalStackLayout. Внутри VerticalStackLayout помещаются одно изображение - элемент Image, две текстовых метки - элементы Label и кнопка - элемент Button.
Таким образом, при запуске приложения мы увидим на экране то содержимое, которое определяется файлом MainPage.xaml:
Стоит отметить, что в принципе нам необязательно использовать подобную конструкцию, когда приложение использует страницу AppShell, которая в свою очередь отображает страницу MainPage. Например, мы могли бы изменить файл App.xaml.cs следующим образом:
namespace HelloApp; public partial class App : Application { public App() { InitializeComponent(); MainPage = new MainPage(); //new AppShell(); } }
В данном случае в качестве главной страницы приложения напрямую устанавливается класс MainPage. В принципе свойству Page класса Application мы можем передать любой объект класса Page, который призван выполнять роль главной страницы.