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
<Border Grid.Row="0" Background="#333"> <TextBlock Text="Chat" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/> </Border><p>И самое классное - не нужно адаптировать этот код под разные разрешения или браузеры.</p>
12
<Border Grid.Row="0" Background="#333"> <TextBlock Text="Chat" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/> </Border><p>И самое классное - не нужно адаптировать этот код под разные разрешения или браузеры.</p>
13
<p>Хотя большая часть задумок реализуется достаточно быстро, код получается громоздким, а иногда все же приходится прибегать к различным хитростям.</p>
13
<p>Хотя большая часть задумок реализуется достаточно быстро, код получается громоздким, а иногда все же приходится прибегать к различным хитростям.</p>
14
<p>Например, в текстовом поле курсор всегда отображается в левом верхнем углу. Это, конечно, можно исправить с помощью Padding, но при изменении размеров поля всё поломается. Поэтому приходится помещать TextBox в контейнер и задавать свойства ему, а само текстовое поле просто выравнивать по центру:</p>
14
<p>Например, в текстовом поле курсор всегда отображается в левом верхнем углу. Это, конечно, можно исправить с помощью Padding, но при изменении размеров поля всё поломается. Поэтому приходится помещать TextBox в контейнер и задавать свойства ему, а само текстовое поле просто выравнивать по центру:</p>
15
<Border BorderBrush="#444" BorderThickness="1" > <TextBox Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/> </Border><p>Тут HTML выигрывает, потому что в нем не нужно создавать дополнительный элемент.</p>
15
<Border BorderBrush="#444" BorderThickness="1" > <TextBox Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/> </Border><p>Тут HTML выигрывает, потому что в нем не нужно создавать дополнительный элемент.</p>
16
<p>Однако это всё цветочки по сравнению с тем, что приходится делать, чтобы настроить отображение кнопок (смотрите ниже).</p>
16
<p>Однако это всё цветочки по сравнению с тем, что приходится делать, чтобы настроить отображение кнопок (смотрите ниже).</p>
17
<p>В XAML можно использовать стили, дающие большие возможности. Например, есть возможность задать свойства для всех кнопок или списков, а можно создать ключ и выборочно указывать его каким-то элементам.</p>
17
<p>В XAML можно использовать стили, дающие большие возможности. Например, есть возможность задать свойства для всех кнопок или списков, а можно создать ключ и выборочно указывать его каким-то элементам.</p>
18
<p>Стили могут наследоваться, и в них можно менять свойства в зависимости от состояния элемента:</p>
18
<p>Стили могут наследоваться, и в них можно менять свойства в зависимости от состояния элемента:</p>
19
<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><p>Можно даже создать триггер, который будет срабатывать, если пользователь введет определенное значение. Ну и, конечно, можно указывать обработчики событий.</p>
19
<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><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
<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><p>То есть создавать три стиля вместо двух.</p>
23
<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><p>То есть создавать три стиля вместо двух.</p>
24
<p>Работа со списками в XAML - это чудо. Можно указать как источник элементов коллекцию объектов, а потом вывести их по шаблону:</p>
24
<p>Работа со списками в XAML - это чудо. Можно указать как источник элементов коллекцию объектов, а потом вывести их по шаблону:</p>
25
<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><p>Вот сами объекты, коллекция и вывод источника:</p>
25
<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><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<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; } }<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<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; } }<p>А таким будет вывод:</p>
27
<p>Написав один раз хороший шаблон, можно почти не возвращаться к нему - только если понадобится вывести новое свойство или изменить дизайн.</p>
27
<p>Написав один раз хороший шаблон, можно почти не возвращаться к нему - только если понадобится вывести новое свойство или изменить дизайн.</p>
28
<p>Иногда использование шаблонов - это необходимость, без которой не обойтись. Например, нужно очень постараться, чтобы кнопка при наведении меняла цвет так, как надо:</p>
28
<p>Иногда использование шаблонов - это необходимость, без которой не обойтись. Например, нужно очень постараться, чтобы кнопка при наведении меняла цвет так, как надо:</p>
29
<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><p>Это очень большой код, поэтому бесит, когда приходится писать его в каждом проекте. Для сравнения, вот как это делается в CSS:</p>
29
<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><p>Это очень большой код, поэтому бесит, когда приходится писать его в каждом проекте. Для сравнения, вот как это делается в CSS:</p>
30
.button { background: #333; } .button:hover { background: #444; }<p>Еще одна крутая штука - привязка данных. Можно в качестве значения текстового поля указать название свойства объекта - и, когда пользователь изменит текст, изменится и объект:</p>
30
.button { background: #333; } .button:hover { background: #444; }<p>Еще одна крутая штука - привязка данных. Можно в качестве значения текстового поля указать название свойства объекта - и, когда пользователь изменит текст, изменится и объект:</p>
31
<TextBox Text="{Binding Name}" Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/><p>Можно привязать это же свойство к другому элементу, который тоже будет меняться:</p>
31
<TextBox Text="{Binding Name}" Background="#333" Foreground="#fff" BorderThickness="0" VerticalAlignment="Center" Padding="2"/><p>Можно привязать это же свойство к другому элементу, который тоже будет меняться:</p>
32
<TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/><p>И вот что получится в результате:</p>
32
<TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="#fff" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16pt" FontWeight="DemiBold"/><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>