Share via


Parte 4. Conceitos básicos da associação de dados

As associações de dados permitem que as propriedades de dois objetos sejam vinculadas para que uma alteração em um cause uma alteração no outro. Essa é uma ferramenta muito valiosa e, embora as associações de dados possam ser definidas inteiramente em código, o XAML fornece atalhos e conveniência. Consequentemente, uma das extensões de marcação mais importantes é Xamarin.Forms Binding.

Ligações de dados

As associações de dados conectam propriedades de dois objetos, chamados de origem e destino. No código, duas etapas são necessárias: A BindingContext propriedade do objeto de destino deve ser definida como o objeto de origem e o SetBinding método (geralmente usado em conjunto com a Binding classe) deve ser chamado no objeto de destino para vincular uma propriedade desse objeto a uma propriedade do objeto de origem.

A propriedade target deve ser uma propriedade vinculável, o que significa que o objeto de destino deve derivar de BindableObject. A documentação on-line Xamarin.Forms indica quais propriedades são propriedades vinculáveis. Uma propriedade de Label tal como Text está associada à propriedade TextPropertyvinculável .

Na marcação, você também deve executar as mesmas duas etapas que são necessárias no código, exceto que a Binding extensão de marcação toma o lugar da SetBinding chamada e da Binding classe.

No entanto, quando você define associações de dados em XAML, há várias maneiras de definir o BindingContext do objeto de destino. Às vezes, ele é definido a partir do arquivo code-behind, às vezes usando uma StaticResource extensão de marcação ou x:Static e, às vezes, como o conteúdo de marcas de elemento de BindingContext propriedade.

As ligações são usadas com mais frequência para conectar os elementos visuais de um programa com um modelo de dados subjacente, geralmente em uma realização da arquitetura de aplicativo MVVM (Model-View-ViewModel), conforme discutido na Parte 5. De Ligações de Dados a MVVM, mas outros cenários são possíveis.

Ligações de exibição para exibição

Você pode definir associações de dados para vincular propriedades de dois modos de exibição na mesma página. Nesse caso, você define o BindingContext do objeto de destino usando a x:Reference extensão de marcação.

Aqui está um arquivo XAML que contém um Slider e dois Label modos de exibição, um dos quais é girado Slider pelo valor e outro que exibe o Slider valor:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderBindingsPage"
             Title="Slider Bindings Page">

    <StackLayout>
        <Label Text="ROTATION"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}"
               FontAttributes="Bold"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

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

        <Label BindingContext="{x:Reference slider}"
               Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
               FontAttributes="Bold"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

O Slider contém um x:Name atributo que é referenciado pelos dois Label modos de exibição usando a extensão de x:Reference marcação.

A x:Reference extensão de vinculação define uma propriedade nomeada Name para definir como o nome do elemento referenciado, neste caso slider. No entanto, a ReferenceExtension classe que define a extensão de x:Reference marcação também define um ContentProperty atributo para Name, o que significa que ele não é explicitamente necessário. Apenas para variedade, o primeiro x:Reference inclui "Name=", mas o segundo não:

BindingContext="{x:Reference Name=slider}"
…
BindingContext="{x:Reference slider}"

A Binding própria extensão de marcação pode ter várias propriedades, assim como a BindingBase classe e Binding . O ContentProperty for Binding é Path, mas a parte "Path=" da extensão de marcação pode ser omitida se o caminho for o primeiro item na Binding extensão de marcação. O primeiro exemplo tem "Path=", mas o segundo exemplo o omite:

Rotation="{Binding Path=Value}"
…
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

As propriedades podem estar todas em uma linha ou separadas em várias linhas:

Text="{Binding Value,
               StringFormat='The angle is {0:F0} degrees'}"

Faça o que for conveniente.

Observe a StringFormat propriedade na segunda Binding extensão de marcação. No Xamarin.Forms, as ligações não executam conversões de tipo implícitas e, se você precisar exibir um objeto que não seja de cadeia de caracteres como uma cadeia de caracteres, deverá fornecer um conversor de tipo ou usar StringFormat. Nos bastidores, o método estático String.Format é usado para implementar StringFormato . Isso é potencialmente um problema, porque as especificações de formatação do .NET envolvem chaves curvas, que também são usadas para delimitar extensões de marcação. Isso cria um risco de confundir o analisador XAML. Para evitar isso, coloque toda a cadeia de formatação entre aspas simples:

Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

Aqui está o programa em execução:

Ligações de exibição para exibição

O Modo de Vinculação

Uma única exibição pode ter associações de dados em várias de suas propriedades. No entanto, cada modo de exibição pode ter apenas um BindingContext, portanto, várias associações de dados nesse modo de exibição devem fazer referência a todas as propriedades do mesmo objeto.

A solução para esse e outros problemas envolve a Mode propriedade, que é definida como um membro da BindingMode enumeração:

  • Default
  • OneWay — os valores são transferidos da origem para o destino
  • OneWayToSource — os valores são transferidos do destino para a origem
  • TwoWay — os valores são transferidos nos dois sentidos entre a origem e o destino
  • OneTime — os dados vão da origem para o destino, mas apenas quando as BindingContext alterações

O programa a seguir demonstra um uso comum dos OneWayToSource modos e TwoWay vinculação. Quatro Slider modos de exibição destinam-se a controlar as Scalepropriedades , Rotate, RotateXe RotateY de um Labelarquivo . A princípio, parece que essas quatro propriedades do devem ser destinos de vinculação de Label dados, porque cada um está sendo definido por um Slider. No entanto, o BindingContext de Label pode ser apenas um objeto, e há quatro controles deslizantes diferentes.

Por essa razão, todas as ligações são definidas de maneiras aparentemente inversas: A BindingContext de cada um dos quatro controles deslizantes é definida como o Label, e as ligações são definidas nas Value propriedades dos controles deslizantes. Usando os OneWayToSource modos e TwoWay , essas Value propriedades podem definir as propriedades de origem, que são as Scalepropriedades , Rotate, RotateXe RotateY do Label:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderTransformsPage"
             Padding="5"
             Title="Slider Transforms Page">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <!-- Scaled and rotated Label -->
        <Label x:Name="label"
               Text="TEXT"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <!-- Slider and identifying Label for Scale -->
        <Slider x:Name="scaleSlider"
                BindingContext="{x:Reference label}"
                Grid.Row="1" Grid.Column="0"
                Maximum="10"
                Value="{Binding Scale, Mode=TwoWay}" />

        <Label BindingContext="{x:Reference scaleSlider}"
               Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
               Grid.Row="1" Grid.Column="1"
               VerticalTextAlignment="Center" />

        <!-- Slider and identifying Label for Rotation -->
        <Slider x:Name="rotationSlider"
                BindingContext="{x:Reference label}"
                Grid.Row="2" Grid.Column="0"
                Maximum="360"
                Value="{Binding Rotation, Mode=OneWayToSource}" />

        <Label BindingContext="{x:Reference rotationSlider}"
               Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
               Grid.Row="2" Grid.Column="1"
               VerticalTextAlignment="Center" />

        <!-- Slider and identifying Label for RotationX -->
        <Slider x:Name="rotationXSlider"
                BindingContext="{x:Reference label}"
                Grid.Row="3" Grid.Column="0"
                Maximum="360"
                Value="{Binding RotationX, Mode=OneWayToSource}" />

        <Label BindingContext="{x:Reference rotationXSlider}"
               Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
               Grid.Row="3" Grid.Column="1"
               VerticalTextAlignment="Center" />

        <!-- Slider and identifying Label for RotationY -->
        <Slider x:Name="rotationYSlider"
                BindingContext="{x:Reference label}"
                Grid.Row="4" Grid.Column="0"
                Maximum="360"
                Value="{Binding RotationY, Mode=OneWayToSource}" />

        <Label BindingContext="{x:Reference rotationYSlider}"
               Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
               Grid.Row="4" Grid.Column="1"
               VerticalTextAlignment="Center" />
    </Grid>
</ContentPage>

As ligações em três das Slider exibições são OneWayToSource, o que significa que o Slider valor causa uma alteração na propriedade de seu BindingContext, que é o Label nome label. Esses três Slider modos de exibição causam alterações nas Rotatepropriedades , RotateXe RotateY do Label.

No entanto, a vinculação para a Scale propriedade é TwoWay. Isso ocorre porque a Scale propriedade tem um valor padrão de 1 e o uso de uma TwoWay associação faz com que o Slider valor inicial seja definido como 1 em vez de 0. Se essa associação fosse OneWayToSource, a Scale propriedade seria inicialmente definida como 0 a partir do Slider valor padrão. O Label não seria visível, e isso pode causar alguma confusão ao usuário.

Ligações invertidas

Observação

A VisualElement classe também tem ScaleX propriedades e ScaleY , que dimensionam o VisualElement no eixo x e eixo y, respectivamente.

Ligações e Coleções

Nada ilustra melhor o poder do XAML e das associações de dados do que um modelo ListView.

ListView define uma ItemsSource propriedade do tipo IEnumerablee exibe os itens dessa coleção. Esses itens podem ser objetos de qualquer tipo. Por padrão, ListView usa o ToString método de cada item para exibir esse item. Às vezes, isso é exatamente o que você deseja, mas, em muitos casos, ToString retorna apenas o nome de classe totalmente qualificado do objeto.

No entanto, os itens na coleção podem ser exibidos da ListView maneira que você desejar por meio do uso de um modelo, que envolve uma classe derivada de Cell. O modelo é clonado para cada item no , e as ListViewassociações de dados que foram definidas no modelo são transferidas para os clones individuais.

Muitas vezes, você desejará criar uma célula personalizada para esses itens usando a ViewCell classe. Esse processo é um pouco confuso no código, mas em XAML ele se torna muito simples.

Incluído no projeto XamlSamples é uma classe chamada NamedColor. Cada NamedColor objeto tem Name e FriendlyName propriedades do tipo string, e uma Color propriedade do tipo Color. Além disso, NamedColor possui 141 campos estáticos somente leitura do tipo Color correspondente às cores definidas na Xamarin.FormsColor classe. Um construtor estático cria uma IEnumerable<NamedColor> coleção que contém NamedColor objetos correspondentes a esses campos estáticos e atribui-a à sua propriedade estática All pública.

Definir a propriedade estática NamedColor.All como a ItemsSource de a ListView é fácil usando a x:Static extensão de marcação:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
             x:Class="XamlSamples.ListViewDemoPage"
             Title="ListView Demo Page">

    <ListView ItemsSource="{x:Static local:NamedColor.All}" />

</ContentPage>

A exibição resultante estabelece que os itens são realmente do tipo XamlSamples.NamedColor:

Vinculação a uma coleção

Não é muita informação, mas o ListView é rolável e selecionável.

Para definir um modelo para os itens, convém dividir a ItemTemplate propriedade como um elemento de propriedade e defini-la como um DataTemplate, que faz referência a um ViewCell. Para a View propriedade do você pode definir um layout de um ou mais modos de ViewCell exibição para exibir cada item. Aqui está um exemplo simples:

<ListView ItemsSource="{x:Static local:NamedColor.All}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <ViewCell.View>
                    <Label Text="{Binding FriendlyName}" />
                </ViewCell.View>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Observação

A fonte de ligação para células, e filhos de células, é a ListView.ItemsSource coleção.

O Label elemento é definido como a View propriedade do ViewCell. (As ViewCell.View marcas não são necessárias porque a View propriedade é a propriedade content de ViewCell.) Essa marcação exibe a FriendlyName propriedade de cada NamedColor objeto:

Vinculando a uma coleção com um DataTemplate

Muito melhor. Agora tudo o que é necessário é enfeitar o modelo de item com mais informações e a cor real. Para dar suporte a esse modelo, alguns valores e objetos foram definidos no dicionário de recursos da página:

<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.ListViewDemoPage"
             Title="ListView Demo Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <OnPlatform x:Key="boxSize"
                        x:TypeArguments="x:Double">
                <On Platform="iOS, Android, UWP" Value="50" />
            </OnPlatform>

            <OnPlatform x:Key="rowHeight"
                        x:TypeArguments="x:Int32">
                <On Platform="iOS, Android, UWP" Value="60" />
            </OnPlatform>

            <local:DoubleToIntConverter x:Key="intConverter" />

        </ResourceDictionary>
    </ContentPage.Resources>

    <ListView ItemsSource="{x:Static local:NamedColor.All}"
              RowHeight="{StaticResource rowHeight}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Padding="5, 5, 0, 5"
                                 Orientation="Horizontal"
                                 Spacing="15">

                        <BoxView WidthRequest="{StaticResource boxSize}"
                                 HeightRequest="{StaticResource boxSize}"
                                 Color="{Binding Color}" />

                        <StackLayout Padding="5, 0, 0, 0"
                                     VerticalOptions="Center">

                            <Label Text="{Binding FriendlyName}"
                                   FontAttributes="Bold"
                                   FontSize="Medium" />

                            <StackLayout Orientation="Horizontal"
                                         Spacing="0">
                                <Label Text="{Binding Color.R,
                                       Converter={StaticResource intConverter},
                                       ConverterParameter=255,
                                       StringFormat='R={0:X2}'}" />

                                <Label Text="{Binding Color.G,
                                       Converter={StaticResource intConverter},
                                       ConverterParameter=255,
                                       StringFormat=', G={0:X2}'}" />

                                <Label Text="{Binding Color.B,
                                       Converter={StaticResource intConverter},
                                       ConverterParameter=255,
                                       StringFormat=', B={0:X2}'}" />
                            </StackLayout>
                        </StackLayout>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

Observe o uso de para definir o tamanho de OnPlatform a BoxView e a altura das ListView linhas. Embora os valores para todas as plataformas sejam os mesmos, a marcação poderia ser facilmente adaptada para outros valores para ajustar a exibição.

Conversores de associação de valor

O arquivo XAML ListView Demo anterior exibe o indivíduo R, Ge B as Xamarin.FormsColor propriedades da estrutura. Essas propriedades são do tipo double e variam de 0 a 1. Se você quiser exibir os valores hexadecimais, você não pode simplesmente usar StringFormat com uma especificação de formatação "X2". Isso só funciona para inteiros e, além disso, os double valores precisam ser multiplicados por 255.

Este pequeno problema foi resolvido com um conversor de valores, também chamado de conversor de ligação. Esta é uma classe que implementa a interface, o IValueConverter que significa que tem dois métodos chamados Convert e ConvertBack. O Convert método é chamado quando um valor é transferido da origem para o destino, o ConvertBack método é chamado para transferências de destino para origem em OneWayToSource ou TwoWay associações:

using System;
using System.Globalization;
using Xamarin.Forms;

namespace XamlSamples
{
    class DoubleToIntConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
                              object parameter, CultureInfo culture)
        {
            double multiplier;

            if (!Double.TryParse(parameter as string, out multiplier))
                multiplier = 1;

            return (int)Math.Round(multiplier * (double)value);
        }

        public object ConvertBack(object value, Type targetType,
                                  object parameter, CultureInfo culture)
        {
            double divider;

            if (!Double.TryParse(parameter as string, out divider))
                divider = 1;

            return ((double)(int)value) / divider;
        }
    }
}

O ConvertBack método não desempenha uma função neste programa porque as ligações são apenas uma maneira de origem para destino.

Uma associação faz referência a um conversor de vinculação com a Converter propriedade. Um conversor de vinculação também pode aceitar um parâmetro especificado com a ConverterParameter propriedade. Para alguma versatilidade, é assim que o multiplicador é especificado. O conversor de vinculação verifica o parâmetro do conversor em busca de um valor válido double .

O conversor é instanciado no dicionário de recursos para que possa ser compartilhado entre várias associações:

<local:DoubleToIntConverter x:Key="intConverter" />

Três associações de dados fazem referência a essa única instância. Observe que a extensão de Binding marcação contém uma extensão de marcação incorporada StaticResource :

<Label Text="{Binding Color.R,
                      Converter={StaticResource intConverter},
                      ConverterParameter=255,
                      StringFormat='R={0:X2}'}" />

Eis o resultado:

Vinculando a uma coleção com um DataTemplate e conversores

O ListView é bastante sofisticado no tratamento de alterações que podem ocorrer dinamicamente nos dados subjacentes, mas apenas se você executar determinadas etapas. Se a coleção de itens atribuídos à ItemsSource propriedade das alterações durante o ListView tempo de execução — ou seja, se os itens puderem ser adicionados ou removidos da coleção — use uma ObservableCollection classe para esses itens. ObservableCollection implementa a INotifyCollectionChanged interface e ListView instalará um manipulador para o CollectionChanged evento.

Se as propriedades dos próprios itens forem alteradas durante o tempo de execução, os itens na coleção deverão implementar a INotifyPropertyChanged interface e sinalizar alterações nos valores de propriedade usando o PropertyChanged evento. Isso é demonstrado na próxima parte desta série, Parte 5. Da vinculação de dados ao MVVM.

Resumo

As associações de dados fornecem um mecanismo poderoso para vincular propriedades entre dois objetos em uma página ou entre objetos visuais e dados subjacentes. Mas quando o aplicativo começa a trabalhar com fontes de dados, um padrão de arquitetura de aplicativo popular começa a surgir como um paradigma útil. Isso é abordado na Parte 5. De ligações de dados a MVVM.