For whom this article: for people who are just starting their acquaintance with technologies using XAML. In order not to complicate the article, I don’t touch on many details like Markup Extensions, resource management, etc. After reading this article, I hope you can understand what is happening under the hood of the XAML parser and more clearly understand how a graph of objects in memory is obtained from your text document with different properties.
XAML is a markup language that came with the first version of WPF from Microsoft. Now it is also used in Silverlight and Windows Phone 7 (essentially the same Silverlight). Thus, now quite a few people are actively using XAML. However, for effective work, it will be useful to understand the concepts that stand behind the language so that individual constructions do not seem strange.
The first is to spend a small lyrical digression. There are two main types of programming languages: imperative and declarative.
Imperative languages ​​are well-known programming languages ​​like C, C ++, C #, Pascal, Basic, and many others. The basic idea is that in an imperative language we say what needs to be done. But we don’t say what should happen (usually we have to describe it and test it in unit tests).
')
The declarative languages, in the opposite direction, allow us to describe the state we want to achieve, but do not require (and usually do not allow) to describe how to come to this state. Examples of such languages: XAML (and generally all based on hierarchical markup XML, HTML, etc.), also SQL.
So what's the difference?
Suppose I want to create a TextBox and ask it in the text "Habr", in C # it will look like this:
var tb = new TextBox(); tb.Text = "Habr";
On XAML, it will look like this:
<TextBox Text="Habr"/>
The difference is obvious. In the first case, I said:
1. Create an instance of the TextBox class and assign it to the variable tb.
2. Assign the property tb.Text to the value “Habr”.
In the second, I said that I wanted to end up with a TextBox with the value “Habr” in the text. And as for how it will be done, I don’t care, this is a XAML parser. Such a long digression is important to understand how the parser works.
So, now a more detailed example, with an explanation of the work of the parser:
<UserControl x:Class="WpfApplication1.UserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <TextBox Text="Habr" Foreground="Yellow"> <TextBox.Background> <SolidColorBrush Color="Red"/> </TextBox.Background> </TextBox> </UserControl>
What is going on here?
Let's start with a simple one: all tags that have no dot "." in XAML, make the parser create an instance of the class How does the parser know which class to create? This is done by setting the mapping between the XML namespaces specified at the beginning of the XAML and the .Net namespaces.
Entries of the form xmlns = "
schemas.microsoft.com/winfx/2006/xaml/presentation " indicate to the parser which namespaces will be used. Moreover, some spaces are associated with .Net default spaces (that is, they do not require specifying a space from .Net), as in this example. "
Schemas.microsoft.com/winfx/2006/xaml/presentation " is associated with the System.Windows.Controls from PresentationFramework.dll. For others, you need to explicitly specify the connection: xmlns: CustomPrefix = "clr-namespace: WpfApplication1"
In this example
- CustomPrefix - any legal ID in XML that you will use to refer to your objects.
- clr-namespace: - a special prefix that indicates that the .Net namespace will go on
- WpfApplication1 is actually your namespace.
After you declare your own namespace, you can create elements from it:
<CustomPrefix:CustomObject/>
So our XAML causes the parser to create an instance of the WpfApplication1.UserControl1 class, then the parser sees that we want the TextBox in the Content property of our control, the parser to do this and so on.
Well, with the objects sorted out. But there are more properties. For properties there are two options for syntax:
- Attributes:
<TextBox Text="Habr"/>
- Tags:
<TextBox> <TextBox.Text>Habr</TextBox.Text> </TextBox>
- There is another option 2.1. When for the most frequently used property you can set the content simply by specifying it inside the object:
<TextBox>Habr</TextBox>
This entry is equivalent to clause 2, because the TextBox object is marked with the attribute [ContentProperty("Text")]
Now more about the two options:
1. In XML attributes, it is quite obvious that only strings can be stored. So whatever type of property you want to set, in fact, you specify a string value. And already during the creation of the object, the parser converts this value from the string to the type that is required.
Primitive types, like int, DateTime, the parser can convert itself, in fact, it simply calls the Parse method of the corresponding type. But what about complex objects?
A simple example:
<TextBox Background="Red"/>
The TextBox.Background property is of type Brush — an abstract class that has several concrete implementations, for example SolidColorBrush, LinerGradientBrush, and others. So how does our string "Red" turn into a subclass of Brush? The converter is responsible for this. To specify which converter to apply for a particular type, a TypeConverterAttribute is installed on the type. For many built-in types, there are already converters, including, as in our example, there is a converter from the string to Brush, which creates the SolidColorBrush instance and sets the color specified in the string to it.
What to do if you want to set the value of a property that is not supported by the standard converter, or just perform some operation on the value before installation? - Use your own converter. To do this, it is enough to implement the IValueConverter interface, write down all the necessary manipulations in it, and then use the converter in XAML as follows:
<TextBox Background="{Binding Source='Red-Green', Converter={StaticResource GradientColorConverter}}">
Of course, this example looks a bit strange, but most often the data is taken from the objects of business logic, then everything will fall into place.
And of course, in order for the example to work, before using the converter you need to add to the resources, for example:
<TextBox Background="{Binding Source='Red-Green', Converter={StaticResource GradientColorConverter}}"> <TextBox.Resources> <WpfApplication1:GradientColorConverter x:Key="GradientColorConverter"/> </TextBox.Resources> </TextBox>
In order not to litter the article, I will not be here to talk in more detail about the resources, it is better to read other articles or examples.
2. The second option is how to set the property value as a complex object: use object syntax:
<TextBox Text="Habr"> <TextBox.Background> <SolidColorBrush Color="Blue"/> </TextBox.Background> </TextBox>
Everything should be clear here. We create a separate tag for the property of the TextBox object, and in it we create an instance of SolidColorBrush or any other Brush subtype with the parameters we need.
On this introduction to the concept of XAML is to finish, I hope after reading this article, some language constructions will become clearer, and most importantly it will be easier to create your own markup.
UPD Updated the part about converters thanks to
afsherman comments.