Parte 2. Sintaxe essencial de XAML
O XAML foi projetado principalmente para instanciar e inicializar objetos. Mas, muitas vezes, as propriedades devem ser definidas como objetos complexos que não podem ser facilmente representados como cadeias de caracteres XML e, às vezes, as propriedades definidas por uma classe devem ser definidas em uma classe filha. Essas duas necessidades exigem os recursos essenciais de sintaxe XAML de elementos de propriedade e propriedades anexadas.
Elementos da propriedade
Em XAML, as propriedades das classes são normalmente definidas como atributos XML:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large"
TextColor="Aqua" />
No entanto, há uma maneira alternativa de definir uma propriedade em XAML. Para tentar essa alternativa com TextColor
o , primeiro exclua a configuração existente TextColor
:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large" />
Abra a tag empty-element Label
separando-a em tags start e end:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
</Label>
Dentro dessas tags, adicione marcas de início e fim que consistem no nome da classe e um nome de propriedade separados por um ponto:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
</Label.TextColor>
</Label>
Defina o valor da propriedade como conteúdo dessas novas tags, da seguinte forma:
<Label Text="Hello, XAML!"
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="Large">
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>
Essas duas maneiras de especificar a TextColor
propriedade são funcionalmente equivalentes, mas não use as duas maneiras para a mesma propriedade porque isso seria efetivamente definir a propriedade duas vezes e pode ser ambíguo.
Com esta nova sintaxe, algumas terminologias úteis podem ser introduzidas:
Label
é um elemento de objeto. É um Xamarin.Forms objeto expresso como um elemento XML.Text
FontAttributes
,VerticalOptions
eFontSize
são atributos de propriedade. Xamarin.Forms São propriedades expressas como atributos XML.- Nesse trecho final,
TextColor
tornou-se um elemento de propriedade. É uma Xamarin.Forms propriedade, mas agora é um elemento XML.
A definição de elementos de propriedade pode, a princípio, parecer uma violação da sintaxe XML, mas não é. O período não tem nenhum significado especial em XML. Para um decodificador XML, Label.TextColor
é simplesmente um elemento filho normal.
Em XAML, no entanto, essa sintaxe é muito especial. Uma das regras para elementos de propriedade é que nada mais pode aparecer na Label.TextColor
marca. O valor da propriedade é sempre definido como conteúdo entre as marcas de início e fim do elemento de propriedade.
Você pode usar a sintaxe de elemento de propriedade em mais de uma propriedade:
<Label Text="Hello, XAML!"
VerticalOptions="Center">
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
</Label>
Ou você pode usar a sintaxe do elemento de propriedade para todas as propriedades:
<Label>
<Label.Text>
Hello, XAML!
</Label.Text>
<Label.FontAttributes>
Bold
</Label.FontAttributes>
<Label.FontSize>
Large
</Label.FontSize>
<Label.TextColor>
Aqua
</Label.TextColor>
<Label.VerticalOptions>
Center
</Label.VerticalOptions>
</Label>
A princípio, a sintaxe do elemento de propriedade pode parecer uma substituição desnecessária para algo comparativamente bastante simples, e nesses exemplos esse é certamente o caso.
No entanto, a sintaxe do elemento de propriedade torna-se essencial quando o valor de uma propriedade é muito complexo para ser expresso como uma cadeia de caracteres simples. Dentro das tags property-element, você pode instanciar outro objeto e definir suas propriedades. Por exemplo, você pode definir explicitamente uma propriedade, como VerticalOptions
um valor com configurações de LayoutOptions
propriedade:
<Label>
...
<Label.VerticalOptions>
<LayoutOptions Alignment="Center" />
</Label.VerticalOptions>
</Label>
Outro exemplo: O Grid
tem duas propriedades nomeadas RowDefinitions
e ColumnDefinitions
. Essas duas propriedades são do tipo RowDefinitionCollection
e ColumnDefinitionCollection
, que são coleções de RowDefinition
e ColumnDefinition
objetos. Você precisa usar a sintaxe do elemento de propriedade para definir essas coleções.
Aqui está o início do arquivo XAML de uma GridDemoPage
classe, mostrando as marcas de elemento de propriedade para as RowDefinitions
coleções e ColumnDefinitions
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
Title="Grid Demo Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
...
</Grid>
</ContentPage>
Observe a sintaxe abreviada para definir células de tamanho automático, células de larguras e alturas de pixels e configurações de estrelas.
Propriedades Anexadas
Você acabou de ver que o Grid
requer elementos de propriedade para as RowDefinitions
coleções e ColumnDefinitions
para definir as linhas e colunas. No entanto, também deve haver alguma maneira de o programador indicar a linha e a coluna onde cada filho do Grid
reside reside.
Dentro da marca para cada filho do Grid
você especifica a linha e a coluna desse filho usando os seguintes atributos:
Grid.Row
Grid.Column
Os valores padrão desses atributos são 0. Você também pode indicar se um filho abrange mais de uma linha ou coluna com estes atributos:
Grid.RowSpan
Grid.ColumnSpan
Esses dois atributos têm valores padrão de 1.
Aqui está o arquivo GridDemoPage.xaml completo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
Title="Grid Demo Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Label Text="Autosized cell"
Grid.Row="0" Grid.Column="0"
TextColor="White"
BackgroundColor="Blue" />
<BoxView Color="Silver"
HeightRequest="0"
Grid.Row="0" Grid.Column="1" />
<BoxView Color="Teal"
Grid.Row="1" Grid.Column="0" />
<Label Text="Leftover space"
Grid.Row="1" Grid.Column="1"
TextColor="Purple"
BackgroundColor="Aqua"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
<Label Text="Span two rows (or more if you want)"
Grid.Row="0" Grid.Column="2" Grid.RowSpan="2"
TextColor="Yellow"
BackgroundColor="Blue"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
<Label Text="Span two columns"
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
TextColor="Blue"
BackgroundColor="Yellow"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
<Label Text="Fixed 100x100"
Grid.Row="2" Grid.Column="2"
TextColor="Aqua"
BackgroundColor="Red"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
As Grid.Row
configurações e Grid.Column
de 0 não são necessárias, mas geralmente são incluídas para fins de clareza.
Veja como fica:
A julgar apenas pela sintaxe, esses Grid.Row
atributos , Grid.Column
, Grid.RowSpan
, e Grid.ColumnSpan
parecem ser campos estáticos ou propriedades de Grid
, mas, curiosamente, Grid
não define nada chamado Row
, Column
, RowSpan
, ou ColumnSpan
.
Em vez disso, Grid
define quatro propriedades vinculáveis chamadas RowProperty
, ColumnProperty
, RowSpanProperty
e ColumnSpanProperty
. Esses são tipos especiais de propriedades vinculáveis conhecidas como propriedades anexadas. Eles são definidos pela classe, Grid
mas definidos em crianças do Grid
.
Quando você deseja usar essas propriedades anexadas no código, a Grid
classe fornece métodos estáticos chamados SetRow
, GetColumn
e assim por diante. Mas em XAML, essas propriedades anexadas são definidas como atributos nos filhos dos nomes de Grid
propriedades simples.
As propriedades anexadas são sempre reconhecíveis em arquivos XAML como atributos que contêm uma classe e um nome de propriedade separados por um ponto. Eles são chamados de propriedades anexadas porque são definidas por uma classe (neste caso, Grid
) mas anexadas a outros objetos (neste caso, filhos do Grid
). Durante o layout, o Grid
pode interrogar os valores dessas propriedades anexadas para saber onde colocar cada filho.
A AbsoluteLayout
classe define duas propriedades anexadas chamadas LayoutBounds
e LayoutFlags
. Aqui está um padrão quadriculado realizado usando os recursos de posicionamento e dimensionamento proporcionais de AbsoluteLayout
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.AbsoluteDemoPage"
Title="Absolute Demo Page">
<AbsoluteLayout BackgroundColor="#FF8080">
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="1, 0, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0, 0.33, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.67, 0.33, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.33, 0.67, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="1, 0.67, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0, 1, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
<BoxView Color="#8080FF"
AbsoluteLayout.LayoutBounds="0.67, 1, 0.25, 0.25"
AbsoluteLayout.LayoutFlags="All" />
</AbsoluteLayout>
</ContentPage>
Aqui está:
Para algo assim, você pode questionar a sabedoria de usar XAML. Certamente, a repetição e regularidade LayoutBounds
do retângulo sugere que ele pode ser melhor realizado em código.
Essa é certamente uma preocupação legítima, e não há nenhum problema em equilibrar o uso de código e marcação ao definir suas interfaces de usuário. É fácil definir alguns dos elementos visuais em XAML e, em seguida, usar o construtor do arquivo code-behind para adicionar mais alguns elementos visuais que podem ser melhor gerados em loops.
Propriedades de Conteúdo
Nos exemplos anteriores, os objetos , e são StackLayout
definidos como a Content
propriedade do ContentPage
, e os filhos desses layouts são, na verdade, itens na Children
coleçãoAbsoluteLayout
. Grid
No entanto, essas Content
e Children
propriedades não estão em nenhum lugar no arquivo XAML.
Você certamente pode incluir as Content
propriedades e Children
como elementos de propriedade, como no exemplo XamlPlusCode :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
Title="XAML + Code Page">
<ContentPage.Content>
<StackLayout>
<StackLayout.Children>
<Slider VerticalOptions="CenterAndExpand"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="valueLabel"
Text="A simple Label"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Click Me!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
Clicked="OnButtonClicked" />
</StackLayout.Children>
</StackLayout>
</ContentPage.Content>
</ContentPage>
A verdadeira questão é: por que esses elementos de propriedade não são necessários no arquivo XAML?
Os elementos definidos para Xamarin.Forms uso em XAML têm permissão para ter uma propriedade sinalizada ContentProperty
no atributo na classe. Se você procurar a ContentPage
classe na documentação on-line Xamarin.Forms , verá este atributo:
[Xamarin.Forms.ContentProperty("Content")]
public class ContentPage : TemplatedPage
Isso significa que as Content
tags property-element não são necessárias. Presume-se que qualquer conteúdo XML que apareça entre as marcas de início e fim ContentPage
seja atribuído à Content
propriedade.
StackLayout
, Grid
, AbsoluteLayout
e RelativeLayout
todos derivam de Layout<View>
, e se você procurar Layout<T>
na Xamarin.Forms documentação, verá outro ContentProperty
atributo:
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout ...
Isso permite que o conteúdo do layout seja adicionado automaticamente à Children
coleção sem marcas explícitas Children
de elemento de propriedade.
Outras classes também têm ContentProperty
definições de atributo. Por exemplo, a propriedade content de Label
é Text
. Verifique a documentação da API para outras pessoas.
Diferenças de plataforma com o OnPlatform
Em aplicativos de página única, é comum definir a Padding
propriedade na página para evitar substituir a barra de status do iOS. No código, você pode usar a Device.RuntimePlatform
propriedade para esta finalidade:
if (Device.RuntimePlatform == Device.iOS)
{
Padding = new Thickness(0, 20, 0, 0);
}
Você também pode fazer algo semelhante em XAML usando as OnPlatform
classes e On
. Primeiro, inclua elementos de propriedade para a Padding
propriedade próxima à parte superior da página:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
</ContentPage.Padding>
...
</ContentPage>
Dentro dessas tags, inclua uma OnPlatform
tag . OnPlatform
é uma classe genérica. Você precisa especificar o argumento de tipo genérico, neste caso, Thickness
, que é o tipo de Padding
propriedade. Felizmente, há um atributo XAML especificamente para definir argumentos genéricos chamado x:TypeArguments
. Isso deve corresponder ao tipo de propriedade que você está definindo:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
OnPlatform
tem uma propriedade chamada Platforms
que é um IList
de On
objetos. Use marcas de elemento de propriedade para essa propriedade:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.Platforms>
</OnPlatform.Platforms>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Agora adicione On
elementos. Para cada um defina a Platform
propriedade e a Value
propriedade como marcação para a Thickness
propriedade:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.Platforms>
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android" Value="0, 0, 0, 0" />
<On Platform="UWP" Value="0, 0, 0, 0" />
</OnPlatform.Platforms>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Essa marcação pode ser simplificada. A propriedade content de é Platforms
, portanto, essas tags de OnPlatform
elemento de propriedade podem ser removidas:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android" Value="0, 0, 0, 0" />
<On Platform="UWP" Value="0, 0, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
A Platform
propriedade de On
é do tipo IList<string>
, portanto, você pode incluir várias plataformas se os valores forem os mesmos:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
<On Platform="Android, UWP" Value="0, 0, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Como o Android e a UWP estão definidos com o valor padrão do Padding
, essa marca pode ser removida:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Essa é a maneira padrão de definir uma propriedade dependente Padding
da plataforma em XAML. Se a Value
configuração não puder ser representada por uma única cadeia de caracteres, você poderá definir elementos de propriedade para ela:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="...">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS">
<On.Value>
0, 20, 0, 0
</On.Value>
</On>
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
Observação
A OnPlatform
extensão de marcação também pode ser usada em XAML para personalizar a aparência da interface do usuário por plataforma. Ele fornece a mesma funcionalidade que as OnPlatform
classes e On
, mas com uma representação mais concisa. Para obter mais informações, consulte Extensão de marcação OnPlatform.
Resumo
Com elementos de propriedade e propriedades anexadas, grande parte da sintaxe XAML básica foi estabelecida. No entanto, às vezes você precisa definir propriedades para objetos de maneira indireta, por exemplo, a partir de um dicionário de recursos. Essa abordagem é abordada na próxima parte, Parte 3. Extensões de marcação XAML.