Compartilhar via


Xamarin.Forms Associações básicas

Uma Xamarin.Forms associação de dados vincula um par de propriedades entre dois objetos, pelo menos um dos quais geralmente é um objeto de interface do usuário. Esses dois objetos são chamados de destino e origem:

  • O destino é o objeto (e a propriedade) no qual a associação de dados é definida.
  • A origem é o objeto (e propriedade) referenciado pela associação de dados.

Essa distinção, às vezes, pode ser um pouco confusa: no caso mais simples, os dados fluem da origem para o destino, o que significa que o valor da propriedade de destino é definido pelo valor da propriedade da origem. No entanto, em alguns casos, os dados podem fluir do destino para a origem ou em ambas os sentidos. Para evitar confusão, tenha em mente que o destino é sempre o objeto no qual a associação de dados está definida, mesmo se estiver fornecendo os dados em vez de recebendo.

Associações com um contexto de associação

Embora as associações de dados geralmente sejam especificadas inteiramente em XAML, é instrutivo observar as associações de dados no código. A página Associação de código básica contém um arquivo XAML com um Label e um Slider:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicCodeBindingPage"
             Title="Basic Code Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="48"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

O Slider é definido para um intervalo de 0 a 360. A intenção deste programa é girar o Label manipulando o Slider.

Sem associações de dados, você definiria o evento ValueChanged do Slider para um manipulador de eventos que acessa a propriedade Value do Slider e define esse valor para a propriedade Rotation do Label. A associação de dados automatiza esse trabalho. O manipulador de eventos e o código dentro dele não são mais necessários.

Você pode definir uma associação em uma instância de qualquer classe que deriva de BindableObject, que inclui os derivativos Element, VisualElement, View e View. A associação é sempre definida no objeto de destino. A associação faz referência ao objeto de origem. Para definir a associação de dados, use os seguintes dois membros da classe de destino:

  • A propriedade BindingContext especifica o objeto de origem.
  • O método SetBinding especifica a propriedade de destino e a propriedade de origem.

Neste exemplo, o Label é o destino da associação e o Slider é a origem da associação. As alterações na origem Slider afetam a rotação do destino Label. Os dados fluem da origem para o destino.

O método SetBinding definido pelo BindableObject tem um argumento do tipo BindingBase do qual a classe Binding deriva, mas há outros métodos SetBinding definidos pela classe BindableObjectExtensions. O arquivo code-behind no exemplo da Associação de código básica usa um método de extensão SetBinding mais simples de sua classe.

public partial class BasicCodeBindingPage : ContentPage
{
    public BasicCodeBindingPage()
    {
        InitializeComponent();

        label.BindingContext = slider;
        label.SetBinding(Label.RotationProperty, "Value");
    }
}

O objeto Label é o destino da associação, portanto, é o objeto no qual essa propriedade é definida e no qual o método é chamado. A propriedade BindingContext indica a origem da associação, que é o Slider.

O método SetBinding é chamado no destino da associação, mas especifica a propriedade de destino e a propriedade de origem. A propriedade de destino é especificada como um objeto BindableProperty: Label.RotationProperty. A propriedade de origem é especificada como uma cadeia de caracteres e indica a propriedade Value de Slider.

O método SetBinding revela uma das regras de associações de dados mais importantes:

A propriedade de destino deve ter suporte de uma propriedade associável.

Essa regra implica que o objeto de destino deve ser uma instância de uma classe que deriva de BindableObject. Confira o artigo Propriedades associáveis para obter uma visão geral de objetos associáveis e propriedades associáveis.

Não há uma regra para a propriedade de origem, que é especificada como uma cadeia de caracteres. Internamente, a reflexão é usada para acessar a propriedade real. Nesse caso específico, no entanto, a propriedade Value também tem suporte de uma propriedade associável.

O código pode ser simplificado um pouco: a propriedade associável RotationProperty é definida por VisualElemente herdada por Label e também por ContentPage, portanto, o nome de classe não é necessário na chamada SetBinding:

label.SetBinding(RotationProperty, "Value");

No entanto, incluir o nome de classe é um bom lembrete do objeto de destino.

Ao manipular Slider, o Label gira de acordo:

Associação de código básica

A página Associação de XAML básica é idêntica à Associação de código básica, exceto que ela define a associação de dados inteira em XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicXamlBindingPage"
             Title="Basic XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Assim como no código, a associação de dados é definida no objeto de destino, que é o Label. Duas extensões de marcação XAML estão envolvidas. Estas são instantaneamente reconhecíveis pelos delimitadores de chaves:

  • A extensão de marcação x:Reference é necessária para fazer referência ao objeto de origem, que é o Slider denominado slider.
  • A extensão de marcação Binding vincula a propriedade Rotation do Label à propriedade Value do Slider.

Para obter mais informações sobre extensões de marcação XAML, confira o artigo Extensões de marcação XAML. A extensão de marcação x:Reference tem suporte da classe ReferenceExtension. Binding tem suporte da classe BindingExtension. Como os prefixos de namespace XML indicam, x:Reference faz parte da especificação XAML 2009, enquanto Binding faz parte do Xamarin.Forms. Observe que não há aspas dentro das chaves.

É fácil esquecer-se da extensão de marcação x:Reference ao definir o BindingContext. É comum definir de modo incorreto a propriedade diretamente para o nome da origem da associação, do seguinte modo:

BindingContext="slider"

Mas isso não é certo. Essa marcação define a propriedade BindingContext para um objeto string cujos caracteres formam a palavra "slider".

Observe que a propriedade de origem é especificada com a propriedade Path do BindingExtension, que corresponde à propriedade Path da classe Binding.

A marcação mostrada na página Associação de XAML básica pode ser simplificada: extensões de marcação XAML, como x:Reference e Binding, podem ter atributos de propriedade de conteúdo definidos, que, para extensões de marcação XAML, significa que o nome da propriedade não precisa aparecer. A propriedade Name é a propriedade de conteúdo do x:Reference, e a propriedade Path é a propriedade de conteúdo do Binding, o que significa que elas podem ser eliminadas das expressões:

<Label Text="TEXT"
       FontSize="80"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       BindingContext="{x:Reference slider}"
       Rotation="{Binding Value}" />

Associações sem um contexto de associação

A propriedade BindingContext é um componente importante de associações de dados, mas nem sempre é necessária. O objeto de origem pode ser especificado na chamada SetBinding ou na extensão de marcação Binding.

Isso é demonstrado no exemplo Associação de código alternativa. O arquivo XAML é semelhante ao exemplo Associação de código básica, exceto que o Slider é definido para controlar a propriedade Scale do Label. Por esse motivo, o Slider é definido para um intervalo de –2 a 2:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeCodeBindingPage"
             Title="Alternative Code Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

O arquivo code-behind define a associação com o método SetBinding definido por BindableObject. O argumento é um construtor para a classe Binding:

public partial class AlternativeCodeBindingPage : ContentPage
{
    public AlternativeCodeBindingPage()
    {
        InitializeComponent();

        label.SetBinding(Label.ScaleProperty, new Binding("Value", source: slider));
    }
}

O construtor Binding tem seis parâmetros, portanto o parâmetro source é especificado com um argumento nomeado. O argumento é o objeto slider.

Executar este programa pode ser um pouco surpreendente:

Associação de código alternativa

A tela do iOS à esquerda mostra como a tela aparece quando a página é exibida pela primeira vez. Onde está o Label?

O problema é que o Slider tem um valor inicial de 0. Isso faz com que a propriedade Scale do Label também seja definida como 0, substituindo o valor padrão de 1. Isso resulta no Label sendo inicialmente invisível. Como demonstram as capturas de tela do Android, é possível manipular o Slider para fazer o Label aparecer novamente, mas o desaparecimento inicial dele é desconcertante.

Você descobrirá no próximo artigo como evitar esse problema ao inicializar Slider do valor padrão da propriedade Scale.

Observação

A classe VisualElement define também as propriedades ScaleX e ScaleY, que podem dimensionar o VisualElement de forma diferente nos sentidos horizontal e vertical.

A página Associação de XAML alternativa mostra a associação equivalente inteiramente em XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeXamlBindingPage"
             Title="Alternative XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="{Binding Source={x:Reference slider},
                               Path=Value}" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Agora a extensão de marcação Binding tem duas propriedades definidas, Source e Path, separadas por uma vírgula. Se você preferir, elas poderão aparecer na mesma linha:

Scale="{Binding Source={x:Reference slider}, Path=Value}" />

A propriedade Source está definida para uma extensão de marcação x:Reference inserida que normalmente tem a mesma sintaxe que a configuração de BindingContext. Observe que não há aspas entre as chaves e as duas propriedades devem ser separadas por uma vírgula.

A propriedade de conteúdo da extensão de marcação Binding é Path, mas a parte Path= da extensão de marcação só poderá ser eliminada se for a primeira propriedade na expressão. Para eliminar a parte Path=, você precisa trocar as duas propriedades:

Scale="{Binding Value, Source={x:Reference slider}}" />

Embora as extensões de marcação XAML geralmente sejam delimitadas por chaves, elas também podem ser expressas como elementos de objeto:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand">
    <Label.Scale>
        <Binding Source="{x:Reference slider}"
                 Path="Value" />
    </Label.Scale>
</Label>

Agora as propriedades Source e Path são os atributos XAML regulares: os valores aparecem entre aspas e os atributos não são separados por uma vírgula. A extensão de marcação x:Reference também pode se tornar um elemento de objeto:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand">
    <Label.Scale>
        <Binding Path="Value">
            <Binding.Source>
                <x:Reference Name="slider" />
            </Binding.Source>
        </Binding>
    </Label.Scale>
</Label>

Essa sintaxe não é comum, mas, às vezes, é necessária quando objetos complexos estão envolvidos.

Os exemplos mostrados até agora definem a propriedade BindingContext e a propriedade Source de Binding para uma extensão de marcação x:Reference para fazer referência a outra exibição na página. Essas duas propriedades são do tipo Object, e elas podem ser definidas para qualquer objeto que inclua as propriedades que são adequadas para as origens da associação.

Nos próximos artigos, você descobrirá que pode definir a propriedade BindingContext ou Source para uma extensão de marcação x:Static para fazer referência ao valor de uma propriedade estática ou campo ou para uma extensão de marcação StaticResource para fazer referência a um objeto armazenado em um dicionário de recursos ou diretamente a um objeto que é normalmente (mas nem sempre) uma instância de um ViewModel.

A propriedade BindingContext também pode ser definida como um objeto Binding, de modo que as propriedades Source e Path de Binding definem o contexto de associação.

Herança de contexto de associação

Neste artigo, você viu que pode especificar o objeto de origem usando a propriedade BindingContext ou a propriedade Source do objeto Binding. Se ambas estiverem definidas, a propriedade Source do Binding terá precedência em relação a BindingContext.

A propriedade BindingContext tem uma característica muito importante:

A configuração da propriedade BindingContext é herdada por meio da árvore visual.

Como você verá, isso pode ser muito útil para simplificar expressões de associação e, em alguns casos, especialmente em cenários MVVM (Model-View-ViewModel), é essencial.

O exemplo Herança de contexto de associação é uma demonstração simples da herança de contexto de associação:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BindingContextInheritancePage"
             Title="BindingContext Inheritance">
    <StackLayout Padding="10">

        <StackLayout VerticalOptions="FillAndExpand"
                     BindingContext="{x:Reference slider}">

            <Label Text="TEXT"
                   FontSize="80"
                   HorizontalOptions="Center"
                   VerticalOptions="EndAndExpand"
                   Rotation="{Binding Value}" />

            <BoxView Color="#800000FF"
                     WidthRequest="180"
                     HeightRequest="40"
                     HorizontalOptions="Center"
                     VerticalOptions="StartAndExpand"
                     Rotation="{Binding Value}" />
        </StackLayout>

        <Slider x:Name="slider"
                Maximum="360" />

    </StackLayout>
</ContentPage>

A propriedade BindingContext do StackLayout é definida para o objeto slider. Esse contexto de associação é herdado por Label e BoxView, que têm suas propriedades Rotation definidas para a propriedade Value de Slider:

Herança de contexto de associação

No próximo artigo, você verá como o modo de associação pode alterar o fluxo de dados entre objetos de origem e destino.

Encontre mais vídeos sobre o Xamarin no Channel 9 e no YouTube.