10 причин моей любви и ненависти к XAML
2026-02-21 03:42 Diff

#статьи

  • 4 окт 2019
  • 0

XAML — неоднозначная технология, которая умудряется дать столько же возможностей, сколько и головной боли. Разбираемся в ее плюсах и минусах.

 vlada_maestro / shutterstock

Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.

XAML — это расширенный язык разметки, который используется в .NET. Он схож с HTML, поэтому достаточно прост в освоении. У XAML есть множество качеств, за которые его можно любить, но у каждого такого качества есть обратная сторона, вызывающая ненависть.

Я собрал 5 причин для любви (и столько же — для ненависти) к XAML.

Можно использовать Grid и выравнивание по горизонтали и вертикали, чтобы создавать адаптивные интерфейсы. Например, минут 15 достаточно, чтобы сверстать приложение с минималистичным дизайном:

Интерфейс отображается так, как было написано, а если его растягивать, то он сохранит пропорции:

Особенно это удобно для вертикального выравнивания, которое может быть непросто реализовать в HTML и CSS. Здесь это делается с помощью одного свойства VerticalAlignment:

<Border Grid.Row="0" Background="#333"> <TextBlock Text="Chat" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/> </Border>

И самое классное — не нужно адаптировать этот код под разные разрешения или браузеры.

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

Например, в текстовом поле курсор всегда отображается в левом верхнем углу. Это, конечно, можно исправить с помощью Padding, но при изменении размеров поля всё поломается. Поэтому приходится помещать TextBox в контейнер и задавать свойства ему, а само текстовое поле просто выравнивать по центру:

<Border BorderBrush="#444" BorderThickness="1" > <TextBox Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/> </Border>

Тут HTML выигрывает, потому что в нем не нужно создавать дополнительный элемент.

Однако это всё цветочки по сравнению с тем, что приходится делать, чтобы настроить отображение кнопок (смотрите ниже).

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

Стили могут наследоваться, и в них можно менять свойства в зависимости от состояния элемента:

<Window.Resources> <Style TargetType="TextBlock"> <Setter Property="TextBlock.FontFamily" Value="Arial"/> </Style> <Style x:Key="Button1"> <EventSetter Event="Button.Click" Handler="Button1_Click"/> </Style> <Style x:Key="MessagesList"> <Style.Setters> <Setter Property="ListView.Foreground" Value="#fff" /> </Style.Setters> <Style.Triggers> <Trigger Property="ListView.IsMouseOver" Value="True"> <Trigger.Setters> <Setter Property="ListView.Foreground" Value="#ccc" /> </Trigger.Setters> </Trigger> </Style.Triggers> </Style> </Window.Resources>

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

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

Также, если у стиля установлен TargetType, ему нельзя указать ключ, и, следовательно, от него нельзя наследовать. Это очень неудобно, если нужно не только задать свойства всем элементам TextBlock, но и наследовать их для дальнейшего использования.

Тогда приходится писать что-то такое:

<Style x:Key="TextBase"> <Setter Property="TextBlock.Margin" Value="5" /> <Setter Property="TextBlock.Foreground" Value="#e2e2e2" /> </Style> <Style BasedOn="{StaticResource TextBase}" TargetType="TextBlock"></Style> <Style x:Key="TextHeader" BasedOn="{StaticResource TextBase}"> <Setter Property="TextBlock.FontWeight" Value="DemiBold"/> </Style>

То есть создавать три стиля вместо двух.

Работа со списками в XAML — это чудо. Можно указать как источник элементов коллекцию объектов, а потом вывести их по шаблону:

<ListView ScrollViewer.VerticalScrollBarVisibility="Auto" Background="#444" BorderThickness="0" Name="MessagesListView"> <ListView.ItemTemplate> <DataTemplate> <Border Background="#353535" Padding="7" CornerRadius="15" MaxWidth="200"> <TextBlock Text="{Binding Text}" Foreground="#fff" FontSize="12pt"/> </Border> </DataTemplate> </ListView.ItemTemplate> </ListView>

Вот сами объекты, коллекция и вывод источника:

public class Message { private string text; public Message(string text) { this.text = text; } public string Text { get { return this.text; } } } public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); List<Message> messages = new List<Message>(); messages.Add(new Message("Hello!")); messages.Add(new Message("How are you?")); messages.Add(new Message("Long time no see")); MessagesListView.ItemsSource = messages; } }

А таким будет вывод:

Написав один раз хороший шаблон, можно почти не возвращаться к нему — только если понадобится вывести новое свойство или изменить дизайн.

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

<Style x:Key="SendButton"> <Style.Setters> <Setter Property="Border.Margin" Value="1" /> <Setter Property="Border.Background" Value="#333" /> <Setter Property="TextBlock.Foreground" Value="#fff" /> <Setter Property="TextBlock.FontWeight" Value="DemiBold"/> <Setter Property="TextBlock.VerticalAlignment" Value="Center"/> <Setter Property="TextBlock.TextAlignment" Value="Center"/> <Setter Property="Border.Padding" Value="10, 5" /> <Setter Property="Button.Template"> <Setter.Value> <ControlTemplate> <Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Border.Padding}"> <TextBlock Text="{TemplateBinding Button.Content}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style.Setters> <Style.Triggers> <Trigger Property="Control.IsMouseOver" Value="true"> <Setter Property="Button.Background" Value="#444"/> </Trigger> </Style.Triggers> </Style>

Это очень большой код, поэтому бесит, когда приходится писать его в каждом проекте. Для сравнения, вот как это делается в CSS:

.button { background: #333; } .button:hover { background: #444; }

Еще одна крутая штука — привязка данных. Можно в качестве значения текстового поля указать название свойства объекта — и, когда пользователь изменит текст, изменится и объект:

<TextBox Text="{Binding Name}" Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/>

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

<TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/>

И вот что получится в результате:

Привязка данных позволяет значительно сократить код и избавить разработчика от решения многих задач. Но проблема в том, что использовать привязку бывает очень сложно. Чаще всего потому, что не всегда понятно, из-за чего вывод работает некорректно. А причин может быть много:

  • неправильно указана привязка;
  • не указан DataContext;
  • передается пустой объект и так далее.

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

public class User : INotifyPropertyChanged { private string name; public User(string name) { this.name = name; } public string Name { get { return this.name; } set { this.name = value; NotifyPropertyChanged(); } } public void NotifyPropertyChanged(string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; }

Еще одна крутая особенность — кроссплатформенность. XAML используется не только в WPF, но и в Xamarin.Forms. Это позволяет разработчикам ПО для компьютеров быстро начать создавать мобильные приложения.

Так, можно один раз написать интерфейс, который будет работать на Android, iOS и UWP (Windows 10). При этом XAML скомпилируется в нативный код для этих платформ.

Только начинаешь вникать в Xamarin.Forms, как XAML делает удар под дых — это не тот XAML, который ты любишь и знаешь.

Можно, конечно, стерпеть, что у некоторых элементов другие названия. Например, что StackPanel превращается в StackLayout. Можно даже привыкнуть к тому, что для одних целей используются разные элементы. Например, в Xamarin.Forms текст выводится с помощью Label, а не TextBlock.

Но самое интересное начинается, когда пытаешься добавить привязку данных, потому что она делается совсем не так, как ты привык. Можно потратить несколько часов, пока не осознаешь, что ты пытаешься использовать MVVM, а нужно — MVVMCross.

XAML — это крутая технология, которую есть за что любить, но есть и за что ненавидеть. Поэтому разработка превращается в эмоциональные качели: сначала ты счастлив, что всё так просто и удобно, а потом тратишь на реализацию небольшой, казалось бы, фичи несколько часов.

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

Бесплатный курс по Python ➞
Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу