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


vlada_maestro / shutterstock
Что такое XAML
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 скомпилируется в нативный код для этих платформ.
Ненавижу
XAML в WPF! = XAML в Xamarin.Forms
Только начинаешь вникать в Xamarin.Forms, как XAML делает удар под дых — это не тот XAML, который ты любишь и знаешь.
Можно, конечно, стерпеть, что у некоторых элементов другие названия. Например, что StackPanel превращается в StackLayout. Можно даже привыкнуть к тому, что для одних целей используются разные элементы. Например, в Xamarin.Forms текст выводится с помощью Label, а не TextBlock.
Но самое интересное начинается, когда пытаешься добавить привязку данных, потому что она делается совсем не так, как ты привык. Можно потратить несколько часов, пока не осознаешь, что ты пытаешься использовать MVVM, а нужно — MVVMCross.
Заключение
XAML — это крутая технология, которую есть за что любить, но есть и за что ненавидеть. Поэтому разработка превращается в эмоциональные качели: сначала ты счастлив, что всё так просто и удобно, а потом тратишь на реализацию небольшой, казалось бы, фичи несколько часов.
Однако, проработав достаточно долго как с HTML, так и с XAML, я все равно выберу второй. Потому что с ним я каждый раз уверен, что все работает так, как мне нужно, и никак не зависит от внешних факторов.