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 VisualElement
e 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:
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 é oSlider
denominadoslider
. - A extensão de marcação
Binding
vincula a propriedadeRotation
doLabel
à propriedadeValue
doSlider
.
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:
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
:
No próximo artigo, você verá como o modo de associação pode alterar o fluxo de dados entre objetos de origem e destino.
Links relacionados
Vídeo relacionados
Encontre mais vídeos sobre o Xamarin no Channel 9 e no YouTube.