Создание XAML и его структура

Часть 1. Начало работы с XAML

Xamarin.Forms в приложении XAML в основном используется для определения визуального содержимого страницы и работает вместе с файлом кода программной части C#.

Файл программной части предоставляет поддержку кода для разметки. Вместе эти два файла способствуют созданию нового определения класса, включающего дочерние представления и инициализацию свойств. В XAML-файле классы и свойства ссылаются на XML-элементы и атрибуты, а также устанавливаются связи между разметкой и кодом.

Создание решения

Чтобы начать редактирование первого XAML-файла, используйте Visual Studio или Visual Studio для Mac для создания нового Xamarin.Forms решения.

xamarin forms xaml

В окне Создать проект в раскрывающемся списке Тип проекта выберите Мобильное приложение, а затем выберите шаблон Мобильное приложение (Xamarin.Forms) и нажмите кнопку Далее:

В окне "Настройка нового проекта" задайте имя ProjectxamlSamples (или любого из вариантов) и нажмите кнопку "Создать".

В диалоговом окне создания кроссплатформенного приложения нажмите кнопку "Пустая" и нажмите кнопку "ОК":

В решении создаются четыре проекта: библиотека XamlSamples .NET Standard, XamlSamples.Android XamlSamples.iOS и решение универсальная платформа Windows XamlSamples.UWP.

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

Если вам не нужно писать код для конкретной платформы, общий проект библиотеки .NET Standard xamlSamples будет тратить практически все время программирования. Эти статьи не будут рисковать за пределами этого проекта.

Структура XAML-файла

В библиотеке XamlSamples .NET Standard есть пара файлов со следующими именами:

  • App.xaml, XAML-файл;
  • App.xaml.cs, файл кода программной части C#, связанный с XAML-файлом.

Щелкните стрелку рядом с App.xaml, чтобы увидеть файл кода программной части.

Файл App.xaml и App.xaml.cs способствуют созданию класса, наследуемого App от Application. Большинство других классов с XAML-файлами способствуют классу, который является производным от ContentPage; эти файлы используют XAML для определения визуального содержимого всей страницы. Это верно для двух других файлов в проекте XamlSamples:

  • MainPage.xaml, XAML-файл;
  • MainPage.xaml.cs— файл кода программной части C#.

Файл MainPage.xaml выглядит следующим образом (хотя форматирование может немного отличаться):

<contentpage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamlSamples"  x:class="XamlSamples.MainPage">
<stacklayout>
<!-- Place new controls here -->
<label text="Welcome to Xamarin Forms!"  verticaloptions="Center" horizontaloptions="Center"></label>
</stacklayout>
</contentpage>

Два объявления пространства xmlns имен XML ссылаются на URI, первый, казалось бы, на веб-сайте Xamarin, а второй — на веб-сайте Майкрософт. Не проверяйте, на что указывают эти URI. Там ничего нет. Они являются просто URI, принадлежащими Xamarin и Майкрософт, и они в основном работают в качестве идентификаторов версий.

Первое объявление пространства имен XML означает, что теги, определенные в XAML-файле без префикса, ссылаются на классы в Xamarin.Forms, например ContentPage. Второе объявление пространства имен определяет префикс x. Он используется для нескольких элементов и атрибутов, встроенных в сам XAML и поддерживаемых другими реализациями XAML. Однако эти элементы и атрибуты немного отличаются в зависимости от года, внедренного в URI. Xamarin.Forms поддерживает спецификацию XAML 2009, но не все.

Объявление local пространства имен позволяет получить доступ к другим классам из проекта библиотеки .NET Standard.

В конце первого тега x префикс используется для атрибута с именем Class. Поскольку использование этого x префикса практически универсально для пространства имен XAML, такие атрибуты XAML, как Class почти всегда называются x:Class.

Атрибут x:Class задает полное имя класса .NET: MainPage класс в XamlSamples пространстве имен. Это означает, что этот XAML-файл определяет новый класс, именованный MainPage в XamlSamples пространстве имен, который является производным от ContentPage тега, в котором x:Class отображается атрибут.

Атрибут x:Class может отображаться только в корневом элементе XAML-файла для определения производного класса C#. Это единственный новый класс, определенный в XAML-файле. Все остальное, которое отображается в XAML-файле, просто создается из существующих классов и инициализировано.

Файл MainPage.xaml.cs выглядит следующим образом (помимо неиспользуемых using директив):

using Xamarin.Forms;


namespace XamlSamples
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

Класс MainPage является производным от ContentPage класса, но обратите внимание на partial определение класса. Это предполагает, что должно быть другое определение разделяемого класса для MainPage, но где это? И что такое InitializeComponent метод?

При Visual Studio сборке проекта он анализирует XAML-файл для создания файла кода C#. При просмотре в каталоге XamlSamples\XamlSamples\obj\Debug вы найдете файл с именем XamlSamples.MainPage.xaml.g.cs. "g" означает созданный. Это другое определение разделяемого MainPage класса, которое содержит определение InitializeComponent метода, вызываемого конструктором MainPage. Затем эти два определения разделяемых MainPage классов можно скомпилировать вместе. В зависимости от того, компилируется ли XAML- или нет, XAML-файл или двоичный формат XAML-файла внедряется в исполняемый файл.

Во время выполнения код в конкретном проекте платформы вызывает LoadApplication метод, передавая ему новый экземпляр App класса в библиотеке .NET Standard. Конструктор App класса создает экземпляр MainPage. Конструктор этого класса вызывает InitializeComponent метод, который затем вызывает LoadFromXaml метод, который извлекает XAML-файл (или его скомпилированный двоичный файл) из библиотеки .NET Standard. LoadFromXaml Инициализирует все объекты, определенные в XAML-файле, соединяет их все в отношениях "родители-потомки", присоединяет обработчики событий, определенные в коде, к событиям, заданным в XAML-файле, и задает результирующий дерево объектов в качестве содержимого страницы.

Хотя обычно не требуется тратить много времени на созданные файлы кода, иногда исключения среды выполнения создаются в коде в созданных файлах, поэтому их следует знать.

При компиляции и запуске этой программы Label элемент отображается в центре страницы, как предполагает XAML:

Для более интересных визуальных элементов все, что вам нужно, является более интересным XAML.

Добавление новых страниц XAML

Чтобы добавить в проект другие классы на основе ContentPage XAML, выберите проект библиотеки .NET Standard XamlSamples, щелкните правой кнопкой мыши и выберите команду "Добавить > новый элемент...". В диалоговом окне "Добавление нового элемента" выберите страницу содержимого элементов >Xamarin.Forms > Visual C# (не страницу содержимого (C#),которая создает страницу только для кода или представление содержимого, которое не является страницей. Присвойте странице имя, например HelloXamlPage:

В проект добавляются два файла: HelloXamlPage.xaml и файл кода программной части HelloXamlPage.xaml.cs.

Настройка содержимого страницы

Измените файл HelloXamlPage.xaml таким образом, чтобы единственными тегами были те, для ContentPage которых и ContentPage.Content:

<contentpage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:class="XamlSamples.HelloXamlPage">
 <contentpage.content>
 </contentpage.content>
</contentpage>

Теги ContentPage.Content являются частью уникального синтаксиса XAML. Во-первых, они могут быть недопустимыми XML, но они являются законными. Период не является специальным символом в XML.

Теги ContentPage.Content называются тегами элементов свойств. Content является свойством ContentPage, и обычно устанавливается для одного представления или макета с дочерними представлениями. Как правило, свойства становятся атрибутами в XAML, но было бы трудно задать Content атрибут сложному объекту. По этой причине свойство выражается как XML-элемент, состоящий из имени класса и имени свойства, разделенного точкой. Content Теперь свойство можно задать между тегами ContentPage.Content следующим образом:

<contentpage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:class="XamlSamples.HelloXamlPage" title="Hello XAML Page">
 <contentpage.content>
 <label text="Hello, XAML!" verticaloptions="Center" horizontaltextalignment="Center" rotation="-15" isvisible="true" fontsize="Large" fontattributes="Bold" textcolor="Blue"></label>
 </contentpage.content>
</contentpage>

Обратите внимание, что Title атрибут был задан в корневом теге.

В настоящее время связь между классами, свойствами и XML должна быть очевидной: Xamarin.Forms класс (например ContentPage, или Label) отображается в XAML-файле в виде XML-элемента. Свойства этого класса, включая TitleContentPage и семь свойств Label, обычно отображаются как атрибуты XML.

Существует множество сочетаний клавиш для задания значений этих свойств. Некоторые свойства являются базовыми типами данных: например, Title свойства Text имеют тип String, Rotation тип Double и IsVisible (который true по умолчанию задан только для иллюстрации) имеет тип Boolean.

Свойство HorizontalTextAlignment имеет тип TextAlignment, который является перечислением. Для свойства любого типа перечисления необходимо указать имя элемента.

Однако для свойств более сложных типов преобразователи используются для синтаксического анализа XAML. Это классы, производные Xamarin.Forms от TypeConverter. Многие из них являются общедоступными классами, но некоторые из них не являются. Для этого конкретного XAML-файла некоторые из этих классов играют роль за кулисами:

  • LayoutOptionsConverterVerticalOptions для свойства
  • FontSizeConverterFontSize для свойства
  • ColorTypeConverterTextColor для свойства

Эти преобразователи управляют допустимым синтаксисом параметров свойств.

Может ThicknessTypeConverter обрабатывать одно, два или четыре числа, разделенные запятыми. Если указано одно число, оно применяется ко всем четырем сторонам. С двумя числами первый — левое и правое заполнение, а второй — сверху и внизу. Четыре числа находятся в порядке слева, сверху, справа и внизу.

Имена LayoutOptionsConverter открытых статических полей LayoutOptions структуры можно преобразовать в значения типа LayoutOptions.

Может FontSizeConverter обрабатывать NamedSize элемент или числовой размер шрифта.

Принимает ColorTypeConverter имена общедоступных статических полей Color структуры или шестнадцатеричных значений RGB, с альфа-каналом или без него, перед которым предшествует знак числа (#). Вот синтаксис без альфа-канала:

TextColor="#rrggbb"

Каждая из маленьких букв представляет собой шестнадцатеричную цифру. Вот как включается альфа-канал:

TextColor="#aarrggbb"/>

Для альфа-канала помните, что FF полностью непрозрачны и 00 полностью прозрачны.

Два других формата позволяют указать только одну шестнадцатеричную цифру для каждого канала:

TextColor="#rgb" TextColor="#argb"

В таких случаях цифра повторяется для формирования значения. Например, #CF3 — это цвет RGB CC-FF-33.

Переход по страницам

При запуске программы MainPage XamlSamples отображается. Чтобы увидеть новое HelloXamlPage, можно задать ее в качестве новой начальной страницы в файле App.xaml.cs или перейти на новую страницу.MainPage

Чтобы реализовать навигацию, сначала измените код в конструкторе App.xaml.cs, NavigationPage чтобы создать объект:

public App()
{
    InitializeComponent();
    MainPage = new NavigationPage(new MainPage());
}

В конструкторе MainPage.xaml.cs можно создать простой Button и использовать обработчик событий для перехода к HelloXamlPage:

public MainPage()
{
    InitializeComponent();


    Button button = new Button
    {
        Text = "Navigate!",
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center
    };


    button.Clicked += async (sender, args) =>
    {
        await Navigation.PushAsync(new HelloXamlPage());
    };


     Content = button;
}

Content Установка свойства страницы заменяет параметр Content свойства в XAML-файле. При компиляции и развертывании новой версии этой программы на экране появится кнопка. При нажатии на нее выполняется HelloXamlPage переход. Ниже приведена итоговая страница iPhone, Android и UWP:

Вы можете вернуться к MainPage использованию кнопки < "Назад" в iOS, с помощью стрелки влево в верхней части страницы или в нижней части телефона в Android или с помощью стрелки влево в верхней части страницы Windows 10.

Вы можете поэкспериментировать с XAML различными способами отрисовки Label. Если необходимо внедрить любые символы Юникода в текст, можно использовать стандартный синтаксис XML. Например, чтобы поместить приветствие в смарт-кавычки, используйте следующую команду:

<label text="&#x201C;Hello, XAML!&#x201D;" …=""></label>

Вот как выглядит следующее:

Взаимодействие XAML и кода

Пример HelloXamlPage содержит только один на Label странице, но это очень необычно. Большинство ContentPage производных присваивают Content свойству определенный вид макета, например StackLayout. Свойство Children определяется StackLayout как тип IList, но на самом деле это объект типа ElementCollection, и эта коллекция может быть заполнена несколькими представлениями или другими макетами. В XAML эти связи "родители-потомки" устанавливаются с обычной XML-иерархией. Ниже приведен XAML-файл для новой страницы с именем XamlPlusCodePage:

<contentpage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:class="XamlSamples.XamlPlusCodePage" title="XAML + Code Page">
  <stacklayout>
  <slider verticaloptions="CenterAndExpand"></slider>
  <label text="A simple Label" font="Large" horizontaloptions="Center" verticaloptions="CenterAndExpand"></label>
  <button text="Click Me!" horizontaloptions="Center" verticaloptions="CenterAndExpand"></button>
  </stacklayout>
</contentpage>

Этот XAML-файл синтаксически завершен и выглядит следующим образом:

Однако, скорее всего, вы считаете эту программу функционально несовместной. Slider Возможно, это должно привести Label к отображению текущего значения, иButton, вероятно, предназначен для выполнения чего-то в программе.

Как вы увидите в части 4. Основные сведения о привязке данных— задание отображения Slider значения, использующее значение Label, можно полностью обрабатывать в XAML с привязкой данных. Но сначала полезно увидеть решение кода. Несмотря на это, обработка щелчка Button определенно требует кода. Это означает, что файл кода программной части должен XamlPlusCodePage содержать обработчики для ValueChanged события Slider и Clicked события Button. Добавим их:

namespace XamlSamples
{
   public partial class XamlPlusCodePage
   {
      public XamlPlusCodePage()
      {
          InitializeComponent();
      }


      void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
      {


      }


      void OnButtonClicked(object sender, EventArgs args)
      {


      }
   }
}

Эти обработчики событий не должны быть общедоступными.

Вернитесь в XAML-файл и SliderButton теги должны включать атрибуты для ValueChanged и Clicked событий, ссылающихся на эти обработчики:

<contentpage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:class="XamlSamples.XamlPlusCodePage" title="XAML + Code Page">
   <stacklayout>
     <slider verticaloptions="CenterAndExpand" valuechanged="OnSliderValueChanged"></slider>
     <label text="A simple Label" font="Large" horizontaloptions="Center" verticaloptions="CenterAndExpand"></label>
     <button text="Click Me!" horizontaloptions="Center" verticaloptions="CenterAndExpand" clicked="OnButtonClicked"></button>
   </stacklayout>
</contentpage>

Обратите внимание, что назначение обработчика событию имеет тот же синтаксис, что и назначение значения свойству.

Если обработчик события ValueChanged будет использовать Label для отображения текущего Slider значения, обработчик должен ссылаться на этот объект из кода. Требуется Label имя, указанное с помощью атрибута x:Name.

<label x:name="valueLabel" text="A simple Label" font="Large" horizontaloptions="Center" verticaloptions="CenterAndExpand"></label>

Префикс x атрибута x:Name указывает, что этот атрибут является встроенным в XAML.

Имя, присвоенное атрибуту x:Name, имеет те же правила, что и имена переменных C#. Например, он должен начинаться с буквы или подчеркивания и не содержать внедренных пробелов.

ValueChanged Теперь обработчик событий может задать LabelSlider новое значение. Новое значение доступно из аргументов события:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
  valueLabel.Text = args.NewValue.ToString("F3");
}

Или обработчик может получить Slider объект, который создает это событие из аргумента sender, и получить Value свойство из этого:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
  valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}

При первом запуске программы значение не отображаетсяSlider, Label так как ValueChanged событие еще не сработало. Но любая манипуляция Slider с данными приводит к отображению значения:

Теперь для Button. Давайте смоделируем ответ на Clicked событие, отображая оповещение с Text кнопкой. Обработчик событий может безопасно привести sender аргумент к аргументу Button, а затем получить доступ к его свойствам:

async void OnButtonClicked(object sender, EventArgs args)
{
   Button button = (Button)sender;
   await DisplayAlert("Clicked!",
     "The button labeled " + button.Text + " has been clicked",
     "OK");
}

Метод определяется так, async как DisplayAlert метод является асинхронным и должен быть предопределен оператором await, который возвращается после завершения метода. Так как этот метод получает событие срабатывания Button из аргумента sender, один и тот же обработчик можно использовать для нескольких кнопок.

Вы видели, что объект, определенный в XAML, может запустить событие, обрабатываемое в файле кода программной части, и что файл кода программной части может получить доступ к объекту, определенному в XAML, с помощью имени, присвоенного ему с атрибутом x:Name. Это два основных способа взаимодействия кода и XAML.

Дополнительные сведения о работе XAML можно получить, проверив созданный файл XamlPlusCode.xaml.g.cs, который теперь включает любое имя, назначенное любому x:Name атрибуту в качестве частного поля. Ниже приведена упрощенная версия этого файла:

public partial class XamlPlusCodePage : ContentPage {


   private Label valueLabel;


   private void InitializeComponent() {
     this.LoadFromXaml(typeof(XamlPlusCodePage));
     valueLabel = this.FindByName("valueLabel");
   }
}

Объявление этого поля позволяет свободно использовать переменную в XamlPlusCodePage любом месте файла разделяемого класса в вашей юрисдикции. Во время выполнения поле назначается после синтаксического анализа XAML. Это означает, что valueLabel поле начинается null, XamlPlusCodePage но допустимо после InitializeComponent вызова.

После InitializeComponent возврата элемента управления обратно в конструктор визуальные элементы страницы были созданы так же, как если бы они были созданы и инициализированы в коде. XAML-файл больше не играет никакой роли в классе. Вы можете управлять этими объектами на странице любым способом, например путем добавления представлений в StackLayoutстраницу или установки Content свойства страницы на что-то еще. Вы можете "ходить по дереву", проверив Content свойство страницы и элементы в Children коллекциях макетов. Таким образом можно задать свойства для представлений, доступных таким образом, или динамически назначить обработчики событий.

Почувствуйте себя свободным. Это ваша страница, а XAML — это только средство для создания содержимого.

Итоги

В этом руководстве вы узнали, как XAML-файл и файл кода способствуют определению класса, а также как взаимодействуют файлы XAML и кода. Но XAML также имеет собственные уникальные синтаксические функции, которые позволяют использовать его очень гибко.

Материалы по теме