HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-21
1 <p><a>#статьи</a></p>
1 <p><a>#статьи</a></p>
2 <ul><li>4 окт 2019</li>
2 <ul><li>4 окт 2019</li>
3 <li>0</li>
3 <li>0</li>
4 </ul><p>XAML - неоднозначная технология, которая умудряется дать столько же возможностей, сколько и головной боли. Разбираемся в ее плюсах и минусах.</p>
4 </ul><p>XAML - неоднозначная технология, которая умудряется дать столько же возможностей, сколько и головной боли. Разбираемся в ее плюсах и минусах.</p>
5 <p> vlada_maestro / shutterstock</p>
5 <p> vlada_maestro / shutterstock</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
6 <p>Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.</p>
7 <p>XAML - это расширенный язык разметки, который используется в <a>.NET</a>. Он схож с <a>HTML</a>, поэтому достаточно прост в освоении. У XAML есть множество качеств, за которые его можно любить, но у каждого такого качества есть обратная сторона, вызывающая ненависть.</p>
7 <p>XAML - это расширенный язык разметки, который используется в <a>.NET</a>. Он схож с <a>HTML</a>, поэтому достаточно прост в освоении. У XAML есть множество качеств, за которые его можно любить, но у каждого такого качества есть обратная сторона, вызывающая ненависть.</p>
8 <p>Я собрал 5 причин для любви (и столько же - для ненависти) к XAML.</p>
8 <p>Я собрал 5 причин для любви (и столько же - для ненависти) к XAML.</p>
9 <p>Можно использовать Grid и выравнивание по горизонтали и вертикали, чтобы создавать адаптивные интерфейсы. Например, минут 15 достаточно, чтобы сверстать приложение с минималистичным дизайном:</p>
9 <p>Можно использовать Grid и выравнивание по горизонтали и вертикали, чтобы создавать адаптивные интерфейсы. Например, минут 15 достаточно, чтобы сверстать приложение с минималистичным дизайном:</p>
10 <p>Интерфейс отображается так, как было написано, а если его растягивать, то он сохранит пропорции:</p>
10 <p>Интерфейс отображается так, как было написано, а если его растягивать, то он сохранит пропорции:</p>
11 <p>Особенно это удобно для вертикального выравнивания, которое может быть непросто реализовать в HTML и CSS. Здесь это делается с помощью одного свойства<em>VerticalAlignment</em>:</p>
11 <p>Особенно это удобно для вертикального выравнивания, которое может быть непросто реализовать в HTML и CSS. Здесь это делается с помощью одного свойства<em>VerticalAlignment</em>:</p>
12 &lt;Border Grid.Row="0" Background="#333"&gt; &lt;TextBlock Text="Chat" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/&gt; &lt;/Border&gt;<p>И самое классное - не нужно адаптировать этот код под разные разрешения или браузеры.</p>
12 &lt;Border Grid.Row="0" Background="#333"&gt; &lt;TextBlock Text="Chat" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/&gt; &lt;/Border&gt;<p>И самое классное - не нужно адаптировать этот код под разные разрешения или браузеры.</p>
13 <p>Хотя большая часть задумок реализуется достаточно быстро, код получается громоздким, а иногда все же приходится прибегать к различным хитростям.</p>
13 <p>Хотя большая часть задумок реализуется достаточно быстро, код получается громоздким, а иногда все же приходится прибегать к различным хитростям.</p>
14 <p>Например, в текстовом поле курсор всегда отображается в левом верхнем углу. Это, конечно, можно исправить с помощью Padding, но при изменении размеров поля всё поломается. Поэтому приходится помещать TextBox в контейнер и задавать свойства ему, а само текстовое поле просто выравнивать по центру:</p>
14 <p>Например, в текстовом поле курсор всегда отображается в левом верхнем углу. Это, конечно, можно исправить с помощью Padding, но при изменении размеров поля всё поломается. Поэтому приходится помещать TextBox в контейнер и задавать свойства ему, а само текстовое поле просто выравнивать по центру:</p>
15 &lt;Border BorderBrush="#444" BorderThickness="1" &gt; &lt;TextBox Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/&gt; &lt;/Border&gt;<p>Тут HTML выигрывает, потому что в нем не нужно создавать дополнительный элемент.</p>
15 &lt;Border BorderBrush="#444" BorderThickness="1" &gt; &lt;TextBox Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/&gt; &lt;/Border&gt;<p>Тут HTML выигрывает, потому что в нем не нужно создавать дополнительный элемент.</p>
16 <p>Однако это всё цветочки по сравнению с тем, что приходится делать, чтобы настроить отображение кнопок (смотрите ниже).</p>
16 <p>Однако это всё цветочки по сравнению с тем, что приходится делать, чтобы настроить отображение кнопок (смотрите ниже).</p>
17 <p>В XAML можно использовать стили, дающие большие возможности. Например, есть возможность задать свойства для всех кнопок или списков, а можно создать ключ и выборочно указывать его каким-то элементам.</p>
17 <p>В XAML можно использовать стили, дающие большие возможности. Например, есть возможность задать свойства для всех кнопок или списков, а можно создать ключ и выборочно указывать его каким-то элементам.</p>
18 <p>Стили могут наследоваться, и в них можно менять свойства в зависимости от состояния элемента:</p>
18 <p>Стили могут наследоваться, и в них можно менять свойства в зависимости от состояния элемента:</p>
19 &lt;Window.Resources&gt; &lt;Style TargetType="TextBlock"&gt; &lt;Setter Property="TextBlock.FontFamily" Value="Arial"/&gt; &lt;/Style&gt; &lt;Style x:Key="Button1"&gt; &lt;EventSetter Event="Button.Click" Handler="Button1_Click"/&gt; &lt;/Style&gt; &lt;Style x:Key="MessagesList"&gt; &lt;Style.Setters&gt; &lt;Setter Property="ListView.Foreground" Value="#fff" /&gt; &lt;/Style.Setters&gt; &lt;Style.Triggers&gt; &lt;Trigger Property="ListView.IsMouseOver" Value="True"&gt; &lt;Trigger.Setters&gt; &lt;Setter Property="ListView.Foreground" Value="#ccc" /&gt; &lt;/Trigger.Setters&gt; &lt;/Trigger&gt; &lt;/Style.Triggers&gt; &lt;/Style&gt; &lt;/Window.Resources&gt;<p>Можно даже создать триггер, который будет срабатывать, если пользователь введет определенное значение. Ну и, конечно, можно указывать обработчики событий.</p>
19 &lt;Window.Resources&gt; &lt;Style TargetType="TextBlock"&gt; &lt;Setter Property="TextBlock.FontFamily" Value="Arial"/&gt; &lt;/Style&gt; &lt;Style x:Key="Button1"&gt; &lt;EventSetter Event="Button.Click" Handler="Button1_Click"/&gt; &lt;/Style&gt; &lt;Style x:Key="MessagesList"&gt; &lt;Style.Setters&gt; &lt;Setter Property="ListView.Foreground" Value="#fff" /&gt; &lt;/Style.Setters&gt; &lt;Style.Triggers&gt; &lt;Trigger Property="ListView.IsMouseOver" Value="True"&gt; &lt;Trigger.Setters&gt; &lt;Setter Property="ListView.Foreground" Value="#ccc" /&gt; &lt;/Trigger.Setters&gt; &lt;/Trigger&gt; &lt;/Style.Triggers&gt; &lt;/Style&gt; &lt;/Window.Resources&gt;<p>Можно даже создать триггер, который будет срабатывать, если пользователь введет определенное значение. Ну и, конечно, можно указывать обработчики событий.</p>
20 <p>Есть и обратная сторона. Например, одному элементу можно задать только один стиль. В HTML же можно указывать сразу несколько классов, к которым стиль будет применен.</p>
20 <p>Есть и обратная сторона. Например, одному элементу можно задать только один стиль. В HTML же можно указывать сразу несколько классов, к которым стиль будет применен.</p>
21 <p>Также, если у стиля установлен TargetType, ему нельзя указать ключ, и, следовательно, от него нельзя наследовать. Это очень неудобно, если нужно не только задать свойства всем элементам TextBlock, но и наследовать их для дальнейшего использования.</p>
21 <p>Также, если у стиля установлен TargetType, ему нельзя указать ключ, и, следовательно, от него нельзя наследовать. Это очень неудобно, если нужно не только задать свойства всем элементам TextBlock, но и наследовать их для дальнейшего использования.</p>
22 <p>Тогда приходится писать что-то такое:</p>
22 <p>Тогда приходится писать что-то такое:</p>
23 &lt;Style x:Key="TextBase"&gt; &lt;Setter Property="TextBlock.Margin" Value="5" /&gt; &lt;Setter Property="TextBlock.Foreground" Value="#e2e2e2" /&gt; &lt;/Style&gt; &lt;Style BasedOn="{StaticResource TextBase}" TargetType="TextBlock"&gt;&lt;/Style&gt; &lt;Style x:Key="TextHeader" BasedOn="{StaticResource TextBase}"&gt; &lt;Setter Property="TextBlock.FontWeight" Value="DemiBold"/&gt; &lt;/Style&gt;<p>То есть создавать три стиля вместо двух.</p>
23 &lt;Style x:Key="TextBase"&gt; &lt;Setter Property="TextBlock.Margin" Value="5" /&gt; &lt;Setter Property="TextBlock.Foreground" Value="#e2e2e2" /&gt; &lt;/Style&gt; &lt;Style BasedOn="{StaticResource TextBase}" TargetType="TextBlock"&gt;&lt;/Style&gt; &lt;Style x:Key="TextHeader" BasedOn="{StaticResource TextBase}"&gt; &lt;Setter Property="TextBlock.FontWeight" Value="DemiBold"/&gt; &lt;/Style&gt;<p>То есть создавать три стиля вместо двух.</p>
24 <p>Работа со списками в XAML - это чудо. Можно указать как источник элементов коллекцию объектов, а потом вывести их по шаблону:</p>
24 <p>Работа со списками в XAML - это чудо. Можно указать как источник элементов коллекцию объектов, а потом вывести их по шаблону:</p>
25 &lt;ListView ScrollViewer.VerticalScrollBarVisibility="Auto" Background="#444" BorderThickness="0" Name="MessagesListView"&gt; &lt;ListView.ItemTemplate&gt; &lt;DataTemplate&gt; &lt;Border Background="#353535" Padding="7" CornerRadius="15" MaxWidth="200"&gt; &lt;TextBlock Text="{Binding Text}" Foreground="#fff" FontSize="12pt"/&gt; &lt;/Border&gt; &lt;/DataTemplate&gt; &lt;/ListView.ItemTemplate&gt; &lt;/ListView&gt;<p>Вот сами объекты, коллекция и вывод источника:</p>
25 &lt;ListView ScrollViewer.VerticalScrollBarVisibility="Auto" Background="#444" BorderThickness="0" Name="MessagesListView"&gt; &lt;ListView.ItemTemplate&gt; &lt;DataTemplate&gt; &lt;Border Background="#353535" Padding="7" CornerRadius="15" MaxWidth="200"&gt; &lt;TextBlock Text="{Binding Text}" Foreground="#fff" FontSize="12pt"/&gt; &lt;/Border&gt; &lt;/DataTemplate&gt; &lt;/ListView.ItemTemplate&gt; &lt;/ListView&gt;<p>Вот сами объекты, коллекция и вывод источника:</p>
26 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&lt;Message&gt; messages = new List&lt;Message&gt;(); messages.Add(new Message("Hello!")); messages.Add(new Message("How are you?")); messages.Add(new Message("Long time no see")); MessagesListView.ItemsSource = messages; } }<p>А таким будет вывод:</p>
26 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&lt;Message&gt; messages = new List&lt;Message&gt;(); messages.Add(new Message("Hello!")); messages.Add(new Message("How are you?")); messages.Add(new Message("Long time no see")); MessagesListView.ItemsSource = messages; } }<p>А таким будет вывод:</p>
27 <p>Написав один раз хороший шаблон, можно почти не возвращаться к нему - только если понадобится вывести новое свойство или изменить дизайн.</p>
27 <p>Написав один раз хороший шаблон, можно почти не возвращаться к нему - только если понадобится вывести новое свойство или изменить дизайн.</p>
28 <p>Иногда использование шаблонов - это необходимость, без которой не обойтись. Например, нужно очень постараться, чтобы кнопка при наведении меняла цвет так, как надо:</p>
28 <p>Иногда использование шаблонов - это необходимость, без которой не обойтись. Например, нужно очень постараться, чтобы кнопка при наведении меняла цвет так, как надо:</p>
29 &lt;Style x:Key="SendButton"&gt; &lt;Style.Setters&gt; &lt;Setter Property="Border.Margin" Value="1" /&gt; &lt;Setter Property="Border.Background" Value="#333" /&gt; &lt;Setter Property="TextBlock.Foreground" Value="#fff" /&gt; &lt;Setter Property="TextBlock.FontWeight" Value="DemiBold"/&gt; &lt;Setter Property="TextBlock.VerticalAlignment" Value="Center"/&gt; &lt;Setter Property="TextBlock.TextAlignment" Value="Center"/&gt; &lt;Setter Property="Border.Padding" Value="10, 5" /&gt; &lt;Setter Property="Button.Template"&gt; &lt;Setter.Value&gt; &lt;ControlTemplate&gt; &lt;Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Border.Padding}"&gt; &lt;TextBlock Text="{TemplateBinding Button.Content}"/&gt; &lt;/Border&gt; &lt;/ControlTemplate&gt; &lt;/Setter.Value&gt; &lt;/Setter&gt; &lt;/Style.Setters&gt; &lt;Style.Triggers&gt; &lt;Trigger Property="Control.IsMouseOver" Value="true"&gt; &lt;Setter Property="Button.Background" Value="#444"/&gt; &lt;/Trigger&gt; &lt;/Style.Triggers&gt; &lt;/Style&gt;<p>Это очень большой код, поэтому бесит, когда приходится писать его в каждом проекте. Для сравнения, вот как это делается в CSS:</p>
29 &lt;Style x:Key="SendButton"&gt; &lt;Style.Setters&gt; &lt;Setter Property="Border.Margin" Value="1" /&gt; &lt;Setter Property="Border.Background" Value="#333" /&gt; &lt;Setter Property="TextBlock.Foreground" Value="#fff" /&gt; &lt;Setter Property="TextBlock.FontWeight" Value="DemiBold"/&gt; &lt;Setter Property="TextBlock.VerticalAlignment" Value="Center"/&gt; &lt;Setter Property="TextBlock.TextAlignment" Value="Center"/&gt; &lt;Setter Property="Border.Padding" Value="10, 5" /&gt; &lt;Setter Property="Button.Template"&gt; &lt;Setter.Value&gt; &lt;ControlTemplate&gt; &lt;Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Border.Padding}"&gt; &lt;TextBlock Text="{TemplateBinding Button.Content}"/&gt; &lt;/Border&gt; &lt;/ControlTemplate&gt; &lt;/Setter.Value&gt; &lt;/Setter&gt; &lt;/Style.Setters&gt; &lt;Style.Triggers&gt; &lt;Trigger Property="Control.IsMouseOver" Value="true"&gt; &lt;Setter Property="Button.Background" Value="#444"/&gt; &lt;/Trigger&gt; &lt;/Style.Triggers&gt; &lt;/Style&gt;<p>Это очень большой код, поэтому бесит, когда приходится писать его в каждом проекте. Для сравнения, вот как это делается в CSS:</p>
30 .button { background: #333; } .button:hover { background: #444; }<p>Еще одна крутая штука - привязка данных. Можно в качестве значения текстового поля указать название свойства объекта - и, когда пользователь изменит текст, изменится и объект:</p>
30 .button { background: #333; } .button:hover { background: #444; }<p>Еще одна крутая штука - привязка данных. Можно в качестве значения текстового поля указать название свойства объекта - и, когда пользователь изменит текст, изменится и объект:</p>
31 &lt;TextBox Text="{Binding Name}" Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/&gt;<p>Можно привязать это же свойство к другому элементу, который тоже будет меняться:</p>
31 &lt;TextBox Text="{Binding Name}" Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/&gt;<p>Можно привязать это же свойство к другому элементу, который тоже будет меняться:</p>
32 &lt;TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/&gt;<p>И вот что получится в результате:</p>
32 &lt;TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/&gt;<p>И вот что получится в результате:</p>
33 <p>Привязка данных позволяет значительно сократить код и избавить разработчика от решения многих задач. Но проблема в том, что использовать привязку бывает очень сложно. Чаще всего потому, что не всегда понятно, из-за чего вывод работает некорректно. А причин может быть много:</p>
33 <p>Привязка данных позволяет значительно сократить код и избавить разработчика от решения многих задач. Но проблема в том, что использовать привязку бывает очень сложно. Чаще всего потому, что не всегда понятно, из-за чего вывод работает некорректно. А причин может быть много:</p>
34 <ul><li>неправильно указана привязка;</li>
34 <ul><li>неправильно указана привязка;</li>
35 <li>не указан DataContext;</li>
35 <li>не указан DataContext;</li>
36 <li>передается пустой объект и так далее.</li>
36 <li>передается пустой объект и так далее.</li>
37 </ul><p>Также каждый раз приходится имплементировать интерфейс<em>INotifyPropertyChanged</em>и писать повторяющийся код для каждого класса, об изменении которого нужно уведомлять.</p>
37 </ul><p>Также каждый раз приходится имплементировать интерфейс<em>INotifyPropertyChanged</em>и писать повторяющийся код для каждого класса, об изменении которого нужно уведомлять.</p>
38 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; }<p>Еще одна крутая особенность - кроссплатформенность. XAML используется не только в WPF, но и в Xamarin.Forms. Это позволяет разработчикам ПО для компьютеров быстро начать создавать мобильные приложения.</p>
38 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; }<p>Еще одна крутая особенность - кроссплатформенность. XAML используется не только в WPF, но и в Xamarin.Forms. Это позволяет разработчикам ПО для компьютеров быстро начать создавать мобильные приложения.</p>
39 <p>Так, можно один раз написать интерфейс, который будет работать на Android, iOS и UWP (Windows 10). При этом XAML скомпилируется в нативный код для этих платформ.</p>
39 <p>Так, можно один раз написать интерфейс, который будет работать на Android, iOS и UWP (Windows 10). При этом XAML скомпилируется в нативный код для этих платформ.</p>
40 <p>Только начинаешь вникать в Xamarin.Forms, как XAML делает удар под дых - это не тот XAML, который ты любишь и знаешь.</p>
40 <p>Только начинаешь вникать в Xamarin.Forms, как XAML делает удар под дых - это не тот XAML, который ты любишь и знаешь.</p>
41 <p>Можно, конечно, стерпеть, что у некоторых элементов другие названия. Например, что StackPanel превращается в StackLayout. Можно даже привыкнуть к тому, что для одних целей используются разные элементы. Например, в Xamarin.Forms текст выводится с помощью Label, а не TextBlock.</p>
41 <p>Можно, конечно, стерпеть, что у некоторых элементов другие названия. Например, что StackPanel превращается в StackLayout. Можно даже привыкнуть к тому, что для одних целей используются разные элементы. Например, в Xamarin.Forms текст выводится с помощью Label, а не TextBlock.</p>
42 <p>Но самое интересное начинается, когда пытаешься добавить привязку данных, потому что она делается совсем не так, как ты привык. Можно потратить несколько часов, пока не осознаешь, что ты пытаешься использовать MVVM, а нужно - MVVMCross.</p>
42 <p>Но самое интересное начинается, когда пытаешься добавить привязку данных, потому что она делается совсем не так, как ты привык. Можно потратить несколько часов, пока не осознаешь, что ты пытаешься использовать MVVM, а нужно - MVVMCross.</p>
43 <p>XAML - это крутая технология, которую есть за что любить, но есть и за что ненавидеть. Поэтому разработка превращается в эмоциональные качели: сначала ты счастлив, что всё так просто и удобно, а потом тратишь на реализацию небольшой, казалось бы, фичи несколько часов.</p>
43 <p>XAML - это крутая технология, которую есть за что любить, но есть и за что ненавидеть. Поэтому разработка превращается в эмоциональные качели: сначала ты счастлив, что всё так просто и удобно, а потом тратишь на реализацию небольшой, казалось бы, фичи несколько часов.</p>
44 <p>Однако, проработав достаточно долго как с HTML, так и с XAML, я все равно выберу второй. Потому что с ним я каждый раз уверен, что все работает так, как мне нужно, и никак не зависит от внешних факторов.</p>
44 <p>Однако, проработав достаточно долго как с HTML, так и с XAML, я все равно выберу второй. Потому что с ним я каждый раз уверен, что все работает так, как мне нужно, и никак не зависит от внешних факторов.</p>
45 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>
45 <a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>