Parte 1. Introdução ao XAML

Baixar exemplo Baixar o exemplo

Em um Xamarin.Forms aplicativo, o XAML é usado principalmente para definir o conteúdo visual de uma página e funciona em conjunto com um arquivo code-behind em C#.

O arquivo code-behind fornece suporte de código para a marcação. Juntos, esses dois arquivos contribuem para uma nova definição de classe que inclui exibições filho e inicialização de propriedade. No arquivo XAML, classes e propriedades são referenciadas com elementos e atributos XML, e os links entre a marcação e o código são estabelecidos.

Criando a solução

Para começar a editar seu primeiro arquivo XAML, use o Visual Studio ou Visual Studio para Mac para criar uma nova Xamarin.Forms solução. (Selecione a guia abaixo correspondente ao seu ambiente.)

No Windows, inicie o Visual Studio 2019 e, na janela inicial, clique em Criar um novo projeto para criar um novo projeto:

Janela Nova Solução

Na janela Criar um novo projeto , selecione Móvel na lista suspensa Tipo de projeto , selecione o modelo Aplicativo Móvel (Xamarin.Forms) e clique no botão Avançar :

Janela Novo Projeto

Na janela Configurar seu novo projeto , defina o Nome do projeto como XamlSamples (ou seja qual for a sua preferência) e clique no botão Criar .

Na caixa de diálogo Novo Aplicativo multiplataforma , clique em Branco e clique no botão OK :

Caixa de diálogo Novo Aplicativo

Quatro projetos são criados na solução: a biblioteca .NET Standard XamlSamples, XamlSamples.Android, XamlSamples.iOS e a solução Plataforma Universal do Windows, XamlSamples.UWP.

Depois de criar a solução XamlSamples , talvez você queira testar seu ambiente de desenvolvimento selecionando os vários projetos de plataforma como o projeto de inicialização da solução e criando e implantando o aplicativo simples criado pelo modelo de projeto em emuladores de telefone ou dispositivos reais.

A menos que você precise escrever código específico da plataforma, o projeto de biblioteca compartilhado XamlSamples .NET Standard é onde você gastará praticamente todo o tempo de programação. Esses artigos não se aventurarão fora desse projeto.

Anatomia de um arquivo XAML

Dentro da biblioteca .NET Standard XamlSamples há um par de arquivos com os seguintes nomes:

  • App.xaml, o arquivo XAML; E
  • App.xaml.cs, um arquivo code-behind em C# associado ao arquivo XAML.

Você precisará clicar na seta ao lado de App.xaml para ver o arquivo code-behind.

App.xaml e App.xaml.cs contribuem para uma classe chamada App que deriva de Application. A maioria das outras classes com arquivos XAML contribui para uma classe derivada de ContentPage; esses arquivos usam XAML para definir o conteúdo visual de uma página inteira. Isso é verdadeiro para os outros dois arquivos no projeto XamlSamples :

  • MainPage.xaml, o arquivo XAML; E
  • MainPage.xaml.cs, o arquivo code-behind do C#.

O arquivo MainPage.xaml tem esta aparência (embora a formatação possa ser um pouco diferente):

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             x:Class="XamlSamples.MainPage">

    <StackLayout>
        <!-- Place new controls here -->
        <Label Text="Welcome to Xamarin Forms!"
               VerticalOptions="Center"
               HorizontalOptions="Center" />
    </StackLayout>

</ContentPage>

As duas declarações de namespace XML (xmlns) referem-se a URIs, a primeira aparentemente no site do Xamarin e a segunda no da Microsoft. Não se incomode em verificar o que esses URIs apontam. Não há nada lá. Eles são simplesmente URIs pertencentes ao Xamarin e à Microsoft e funcionam basicamente como identificadores de versão.

A primeira declaração de namespace XML significa que as marcas definidas no arquivo XAML sem prefixo referem-se a classes no Xamarin.Forms, por exemplo ContentPage. A segunda declaração de namespace define um prefixo de x. Isso é usado para vários elementos e atributos que são intrínsecos ao próprio XAML e que têm suporte de outras implementações de XAML. No entanto, esses elementos e atributos são ligeiramente diferentes dependendo do ano inserido no URI. Xamarin.Forms dá suporte à especificação XAML 2009, mas não a todos.

A local declaração de namespace permite que você acesse outras classes do projeto de biblioteca do .NET Standard.

No final dessa primeira marca, o x prefixo é usado para um atributo chamado Class. Como o uso desse x prefixo é virtualmente universal para o namespace XAML, atributos XAML como Class são quase sempre chamados de x:Class.

O x:Class atributo especifica um nome de classe .NET totalmente qualificado: a MainPage classe no XamlSamples namespace . Isso significa que esse arquivo XAML define uma nova classe chamada MainPage no XamlSamples namespace que deriva de ContentPage— a marca na qual o x:Class atributo aparece.

O x:Class atributo só pode aparecer no elemento raiz de um arquivo XAML para definir uma classe C# derivada. Essa é a única nova classe definida no arquivo XAML. Todo o resto que aparece no arquivo XAML é simplesmente instanciado de classes existentes e inicializado.

O arquivo MainPage.xaml.cs tem esta aparência (além das diretivas using não utilizados):

using Xamarin.Forms;

namespace XamlSamples
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
}

A MainPage classe deriva de ContentPage, mas observe a definição de partial classe. Isso sugere que deve haver outra definição de classe parcial para MainPage, mas onde está? E o que é esse InitializeComponent método?

Quando o Visual Studio compila o projeto, ele analisa o arquivo XAML para gerar um arquivo de código C#. Se você examinar o diretório XamlSamples\XamlSamples\obj\Debug , encontrará um arquivo chamado XamlSamples.MainPage.xaml.g.cs. O 'g' significa gerado. Essa é a outra definição de classe parcial de MainPage que contém a definição do InitializeComponent método chamado do MainPage construtor. Essas duas definições de classe parciais MainPage podem ser compiladas juntas. Dependendo se o XAML é compilado ou não, o arquivo XAML ou uma forma binária do arquivo XAML é inserido no executável.

Em runtime, o código no projeto de plataforma específico chama um LoadApplication método , passando para ele uma nova instância da App classe na biblioteca .NET Standard. O App construtor de classe cria uma instância MainPagede . O construtor dessa classe chama InitializeComponent, que chama o LoadFromXaml método que extrai o arquivo XAML (ou seu binário compilado) da biblioteca .NET Standard. LoadFromXaml inicializa todos os objetos definidos no arquivo XAML, conecta todos eles em relações pai-filho, anexa manipuladores de eventos definidos no código a eventos definidos no arquivo XAML e define a árvore resultante de objetos como o conteúdo da página.

Embora você normalmente não precise gastar muito tempo com arquivos de código gerados, às vezes, exceções de runtime são geradas no código nos arquivos gerados, portanto, você deve estar familiarizado com eles.

Quando você compila e executa este programa, o Label elemento aparece no centro da página, como o XAML sugere:

Exibição padrão Xamarin.Forms

Para visuais mais interessantes, tudo o que você precisa é de XAML mais interessante.

Adicionando novas páginas XAML

Para adicionar outras classes baseadas em ContentPage XAML ao seu projeto, selecione o projeto de biblioteca do .NET Standard XamlSamples , clique com o botão direito do mouse e selecione Adicionar > Novo Item.... Na caixa de diálogo Adicionar Novo Item , selecione Página de Conteúdo de Itens >Xamarin.Forms> do Visual C# (não Página de Conteúdo (C#), que cria uma página somente código ou Exibição de Conteúdo, que não é uma página). Dê um nome à página, por exemplo, HelloXamlPage:

Caixa de diálogo Adicionar Novo Item

Dois arquivos são adicionados ao projeto, HelloXamlPage.xaml e o arquivo code-behind HelloXamlPage.xaml.cs.

Configurando conteúdo da página

Edite o arquivo HelloXamlPage.xaml para que as únicas marcas sejam aquelas para ContentPage e ContentPage.Content:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage">
    <ContentPage.Content>

    </ContentPage.Content>
</ContentPage>

As ContentPage.Content marcas fazem parte da sintaxe exclusiva do XAML. No início, eles podem parecer XML inválidos, mas são legais. O ponto não é um caractere especial em XML.

As ContentPage.Content marcas são chamadas de marcas de elemento de propriedade . Content é uma propriedade de ContentPagee geralmente é definido como um único modo de exibição ou um layout com exibições filho. Normalmente, as propriedades se tornam atributos em XAML, mas seria difícil definir um Content atributo para um objeto complexo. Por esse motivo, a propriedade é expressa como um elemento XML que consiste no nome da classe e no nome da propriedade separado por um ponto. Agora, a Content propriedade pode ser definida entre as ContentPage.Content marcas, desta forma:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.HelloXamlPage"
             Title="Hello XAML Page">
    <ContentPage.Content>

        <Label Text="Hello, XAML!"
               VerticalOptions="Center"
               HorizontalTextAlignment="Center"
               Rotation="-15"
               IsVisible="true"
               FontSize="Large"
               FontAttributes="Bold"
               TextColor="Blue" />

    </ContentPage.Content>
</ContentPage>

Observe também que um Title atributo foi definido na marca raiz.

Neste momento, a relação entre classes, propriedades e XML deve ser evidente: uma Xamarin.Forms classe (como ContentPage ou Label) aparece no arquivo XAML como um elemento XML. As propriedades dessa classe, incluindo Title em ContentPage e sete propriedades de Label, geralmente aparecem como atributos XML.

Existem muitos atalhos para definir os valores dessas propriedades. Algumas propriedades são tipos de dados básicos: por exemplo, as propriedades e Text são do tipo String, Rotation é do tipo Doublee IsVisible (que é true por padrão e é definida aqui apenas para ilustração) é do tipo Boolean.Title

A HorizontalTextAlignment propriedade é do tipo TextAlignment, que é uma enumeração . Para uma propriedade de qualquer tipo de enumeração, tudo o que você precisa fornecer é um nome de membro.

No entanto, para propriedades de tipos mais complexos, os conversores são usados para analisar o XAML. Essas são classes em Xamarin.Forms que derivam de TypeConverter. Muitas são classes públicas, mas outras não. Para esse arquivo XAML específico, várias dessas classes desempenham um papel nos bastidores:

  • LayoutOptionsConverter para a VerticalOptions propriedade
  • FontSizeConverter para a FontSize propriedade
  • ColorTypeConverter para a TextColor propriedade

Esses conversores regem a sintaxe permitida das configurações de propriedade.

O ThicknessTypeConverter pode manipular um, dois ou quatro números separados por vírgulas. Se um número for fornecido, ele se aplicará a todos os quatro lados. Com dois números, o primeiro é preenchimento esquerdo e direito, e o segundo é superior e inferior. Quatro números estão na ordem esquerda, superior, direita e inferior.

O LayoutOptionsConverter pode converter os nomes de campos estáticos públicos da LayoutOptions estrutura em valores do tipo LayoutOptions.

O FontSizeConverter pode manipular um NamedSize membro ou um tamanho de fonte numérico.

O ColorTypeConverter aceita os nomes de campos estáticos públicos da Color estrutura ou valores RGB hexadecimais, com ou sem um canal alfa, precedidos por um sinal numérico (#). Esta é a sintaxe sem um canal alfa:

TextColor="#rrggbb"

Cada uma das letras pequenas é um dígito hexadecimal. Veja como um canal alfa é incluído:

TextColor="#aarrggbb">

Para o canal alfa, tenha em mente que FF é totalmente opaco e 00 é totalmente transparente.

Dois outros formatos permitem que você especifique apenas um único dígito hexadecimal para cada canal:

TextColor="#rgb" TextColor="#argb"

Nesses casos, o dígito é repetido para formar o valor. Por exemplo, #CF3 é a cor RGB CC-FF-33.

Quando você executa o programa XamlSamples , o MainPage é exibido. Para ver o novo HelloXamlPage , você pode definir isso como a nova página de inicialização no arquivo App.xaml.cs ou navegar até a nova página de MainPage.

Para implementar a navegação, primeiro altere o código no construtor App.xaml.cs para que um NavigationPage objeto seja criado:

public App()
{
    InitializeComponent();
    MainPage = new NavigationPage(new MainPage());
}

No construtor MainPage.xaml.cs , você pode criar um simples Button e usar o manipulador de eventos para navegar até HelloXamlPage:

public MainPage()
{
    InitializeComponent();

    Button button = new Button
    {
        Text = "Navigate!",
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center
    };

    button.Clicked += async (sender, args) =>
    {
        await Navigation.PushAsync(new HelloXamlPage());
    };

    Content = button;
}

Definir a Content propriedade da página substitui a configuração da Content propriedade no arquivo XAML. Quando você compila e implanta a nova versão deste programa, um botão aparece na tela. Pressioná-lo navega até HelloXamlPage. Aqui está a página resultante no iPhone, Android e UWP:

Texto do rótulo girado

Você pode navegar de volta para MainPage usando o < botão Voltar no iOS, usando a seta para a esquerda na parte superior da página ou na parte inferior do telefone no Android ou usando a seta para a esquerda na parte superior da página no Windows 10.

Fique à vontade para experimentar o XAML de diferentes maneiras de renderizar o Label. Se você precisar inserir qualquer caractere Unicode no texto, poderá usar a sintaxe XML padrão. Por exemplo, para colocar a saudação entre aspas inteligentes, use:

<Label Text="&#x201C;Hello, XAML!&#x201D;" … />

Esta é a aparência dele:

Texto de rótulo girado com caracteres Unicode

Interações de código e XAML

O exemplo HelloXamlPage contém apenas um único Label na página, mas isso é muito incomum. A maioria dos ContentPage derivativos define a Content propriedade como um layout de algum tipo, como um StackLayout. A Children propriedade do StackLayout é definida como do tipo IList<View> , mas na verdade é um objeto do tipo ElementCollection<View>e essa coleção pode ser preenchida com vários modos de exibição ou outros layouts. Em XAML, essas relações pai-filho são estabelecidas com a hierarquia XML normal. Aqui está um arquivo XAML para uma nova página chamada XamlPlusCodePage:

<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">
    <StackLayout>
        <Slider VerticalOptions="CenterAndExpand" />

        <Label Text="A simple Label"
               Font="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Este arquivo XAML está sintaticamente completo e é assim que ele se parece:

Vários controles em uma página

No entanto, é provável que você considere esse programa como funcionalmente deficiente. Talvez seja Slider suposto fazer com que o Label exiba o valor atual e o Button provavelmente se destina a fazer algo dentro do programa.

Como você verá na Parte 4. Noções básicas de associação de dados, o trabalho de exibir um Slider valor usando um Label pode ser tratado inteiramente em XAML com uma associação de dados. Mas é útil ver a solução de código primeiro. Mesmo assim, lidar com o Button clique definitivamente requer código. Isso significa que o arquivo code-behind para XamlPlusCodePage deve conter manipuladores para o ValueChanged evento do Slider e o Clicked evento do Button. Vamos adicioná-los:

namespace XamlSamples
{
    public partial class XamlPlusCodePage
    {
        public XamlPlusCodePage()
        {
            InitializeComponent();
        }

        void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
        {

        }

        void OnButtonClicked(object sender, EventArgs args)
        {

        }
    }
}

Esses manipuladores de eventos não precisam ser públicos.

De volta ao arquivo XAML, as Slider marcas e Button precisam incluir atributos para os eventos e Clicked que fazem referência a ValueChanged esses manipuladores:

<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">
    <StackLayout>
        <Slider VerticalOptions="CenterAndExpand"
                ValueChanged="OnSliderValueChanged" />

        <Label Text="A simple Label"
               Font="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Click Me!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                Clicked="OnButtonClicked" />
    </StackLayout>
</ContentPage>

Observe que atribuir um manipulador a um evento tem a mesma sintaxe que atribuir um valor a uma propriedade.

Se o manipulador do ValueChanged evento do Slider estiver usando o Label para exibir o valor atual, o manipulador precisará referenciar esse objeto do código. O Label precisa de um nome, que é especificado com o x:Name atributo .

<Label x:Name="valueLabel"
       Text="A simple Label"
       Font="Large"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />

O x prefixo do x:Name atributo indica que esse atributo é intrínseco ao XAML.

O nome que você atribui ao x:Name atributo tem as mesmas regras que nomes de variáveis C#. Por exemplo, ele deve começar com uma letra ou sublinhado e não conter espaços inseridos.

Agora, o ValueChanged manipulador de eventos pode definir o Label para exibir o novo Slider valor. O novo valor está disponível nos argumentos do evento:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = args.NewValue.ToString("F3");
}

Ou o manipulador pode obter o Slider objeto que está gerando esse evento do sender argumento e obter a Value propriedade a partir disso:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    valueLabel.Text = ((Slider)sender).Value.ToString("F3");
}

Quando você executa o programa pela primeira vez, o Label não exibe o Slider valor porque o ValueChanged evento ainda não foi acionado. Mas qualquer manipulação do Slider faz com que o valor seja exibido:

Valor do controle deslizante exibido

Agora, para o Button. Vamos simular uma resposta a um Clicked evento exibindo um alerta com o Text do botão. O manipulador de eventos pode converter com segurança o sender argumento em um Button e acessar suas propriedades:

async void OnButtonClicked(object sender, EventArgs args)
{
    Button button = (Button)sender;
    await DisplayAlert("Clicked!",
        "The button labeled '" + button.Text + "' has been clicked",
        "OK");
}

O método é definido como async porque o DisplayAlert método é assíncrono e deve ser precedido pelo await operador , que retorna quando o método é concluído. Como esse método obtém o Button acionamento do evento do sender argumento , o mesmo manipulador pode ser usado para vários botões.

Você viu que um objeto definido em XAML pode disparar um evento que é manipulado no arquivo code-behind e que o arquivo code-behind pode acessar um objeto definido em XAML usando o nome atribuído a ele com o x:Name atributo . Essas são as duas maneiras fundamentais pelas quais o código e o XAML interagem.

Alguns insights adicionais sobre como o XAML funciona podem ser obtidos examinando o arquivo XamlPlusCode.xaml.g.cs recém-gerado, que agora inclui qualquer nome atribuído a qualquer x:Name atributo como um campo privado. Aqui está uma versão simplificada desse arquivo:

public partial class XamlPlusCodePage : ContentPage {

    private Label valueLabel;

    private void InitializeComponent() {
        this.LoadFromXaml(typeof(XamlPlusCodePage));
        valueLabel = this.FindByName<Label>("valueLabel");
    }
}

A declaração desse campo permite que a variável seja usada livremente em qualquer lugar dentro do XamlPlusCodePage arquivo de classe parcial sob sua jurisdição. Em runtime, o campo é atribuído depois que o XAML é analisado. Isso significa que o valueLabel campo é null quando o XamlPlusCodePage construtor começa, mas é válido depois InitializeComponent de ser chamado.

Depois que InitializeComponent retorna o controle de volta para o construtor, os visuais da página foram construídos como se tivessem sido instanciados e inicializados no código. O arquivo XAML não desempenha mais nenhuma função na classe . Você pode manipular esses objetos na página da maneira desejada, por exemplo, adicionando exibições ao StackLayoutou definindo a Content propriedade da página como algo totalmente diferente. Você pode "andar na árvore" examinando a Content propriedade da página e os itens nas Children coleções de layouts. Você pode definir propriedades em modos de exibição acessados dessa maneira ou atribuir manipuladores de eventos a eles dinamicamente.

Fique à vontade. É sua página, e XAML é apenas uma ferramenta para criar seu conteúdo.

Resumo

Com esta introdução, você viu como um arquivo XAML e um arquivo de código contribuem para uma definição de classe e como os arquivos XAML e de código interagem. Mas o XAML também tem seus próprios recursos sintáticos exclusivos que permitem que ele seja usado de maneira muito flexível. Você pode começar a explorá-los na Parte 2. Sintaxe XAML essencial.