Partilhar via


Noções básicas de vinculação de dados

Visualizar exemplo. Visualizar o exemplo

As associações de dados .NET Multi-platform App UI (.NET MAUI) 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.

Associaçõ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:

  1. A BindingContext propriedade do objeto de destino deve ser definida como o objeto de origem,
  2. 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 de destino deve ser uma propriedade vinculável, o que significa que o objeto de destino deve derivar de BindableObject. Uma propriedade de Label, como Text, está associada à propriedade vinculável TextProperty.

Em XAML, deve-se também executar as mesmas duas etapas necessárias no código, exceto que a extensão de marcação Binding substitui a chamada SetBinding e a classe Binding. 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, é definido a partir do ficheiro code-behind, às vezes usando uma extensão de marcação StaticResource ou x:Static, e às vezes como conteúdo de tags de elemento BindingContext de propriedade.

Observação

As expressões de vinculação de dados nesta página usam associações compiladas. Para obter mais informações sobre associações compiladas, consulte Associações compiladas.

Ligações entre vistas

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.

O exemplo a seguir contém uma Slider e duas Label vistas, uma delas está rotacionada pelo valor Slider e outra que exibe o valor Slider.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderBindingsPage"
             Title="Slider Bindings Page"
             x:DataType="Slider">
    <StackLayout>
        <Label Text="ROTATION"
               BindingContext="{x:Reference slider}"
               Rotation="{Binding Path=Value}"
               FontAttributes="Bold"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="Center" />
        <Label BindingContext="{x:Reference slider}"
               Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
               FontAttributes="Bold"
               FontSize="18"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    </StackLayout>
</ContentPage>

O Slider contém um atributo x:Name que é referenciado pelas duas Label visualizações usando a marcação por extensão x:Reference. A x:Reference extensão binding define uma propriedade nomeada Name para definir 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 não é explicitamente necessário.

A extensão de marcação Binding em si pode ter várias propriedades, assim como a classe BindingBase e a Binding. O ContentProperty para Binding é Path, mas a parte "Path=" da extensão de marcação pode ser omitida se o caminho for o primeiro item na extensão de marcação Binding.

A segunda Binding extensão de marcação define a StringFormat propriedade. No .NET MAUI, as associaçõ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.

Importante

As cadeias de caracteres de formatação devem ser colocadas entre aspas simples.

Modo de ligação

Uma única vista pode ter associações de dados sobre várias das 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 propriedades do mesmo objeto.

A solução para este 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 alvo para a fonte
  • TwoWay — os valores são transferidos nos dois sentidos entre a origem e o destino
  • OneTime — os dados passam da fonte para o destino, mas apenas quando a BindingContext altera

Uma utilização comum dos modos de vinculação OneWayToSource e TwoWay é demonstrada no exemplo a seguir:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             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"
                x:DataType="Label"
                BindingContext="{x:Reference label}"
                Grid.Row="1" Grid.Column="0"
                Maximum="10"
                Value="{Binding Scale, Mode=TwoWay}" />
        <Label x:DataType="Slider"
               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"
                x:DataType="Label"
                BindingContext="{x:Reference label}"
                Grid.Row="2" Grid.Column="0"
                Maximum="360"
                Value="{Binding Rotation, Mode=OneWayToSource}" />
        <Label x:DataType="Slider"
               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"
                x:DataType="Label"
                BindingContext="{x:Reference label}"
                Grid.Row="3" Grid.Column="0"
                Maximum="360"
                Value="{Binding RotationX, Mode=OneWayToSource}" />
        <Label x:DataType="Slider"
               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"
                x:DataType="Label"
                BindingContext="{x:Reference label}"
                Grid.Row="4" Grid.Column="0"
                Maximum="360"
                Value="{Binding RotationY, Mode=OneWayToSource}" />
        <Label x:DataType="Slider"
               BindingContext="{x:Reference rotationYSlider}"
               Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
               Grid.Row="4" Grid.Column="1"
               VerticalTextAlignment="Center" />
    </Grid>
</ContentPage>

Neste exemplo, quatro Slider visões destinam-se a controlar as propriedades Scale, Rotate, RotateX e RotateY de um Label. A princípio, parece como se essas quatro propriedades do Label devessem ser alvos de ligação de dados, porque cada uma está sendo definida por um Slider. No entanto, o BindingContext de Label pode ser apenas um objeto, e há quatro controles deslizantes diferentes. Por esse motivo, o BindingContext de cada um dos quatro controles deslizantes é definido como o Label, e as ligações são definidas nas Value propriedades dos controles deslizantes. Usando os modos OneWayToSource e TwoWay, estas propriedades Value podem definir as propriedades de origem, que são as propriedades Scale, Rotate, RotateX e RotateY do Label.

As ligações em três das visões de Slider são OneWayToSource, o que significa que o valor de Slider causa uma alteração na propriedade de seu BindingContext, que é o Label nomeado label. Essas três Slider vistas causam alterações nas propriedades Rotate, RotateX e RotateY do Label:

Ligações inversas.

No entanto, a vinculação para a Scale propriedade é TwoWay. Isso ocorre porque a Scale propriedade tem um valor padrão de 1 e usar 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 propriedade Scale seria inicialmente definida como 0 a partir do valor padrão Slider. O Label não seria visível.

Observação

A VisualElement classe também tem as propriedades ScaleX e ScaleY, que escalam o VisualElement nos eixos x e y, respectivamente.

Vinculações e coleções

ListView define uma ItemsSource propriedade do tipo IEnumerable, e 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ê quer, mas, em muitos casos, ToString retorna apenas o nome de classe totalmente qualificado do objeto.

No entanto, os itens na ListView coleção podem ser exibidos da maneira que desejar através da utilização de um modelo, que envolve uma classe que deriva de Cell. O modelo é clonado para cada item no ListView, e as ligações de dados que foram definidas no modelo são transferidas para clones individuais. Células personalizadas podem ser criadas para itens usando a ViewCell classe.

ListView pode exibir uma lista de todas as cores nomeadas disponíveis no .NET MAUI, com a ajuda da NamedColor classe:

using System.Reflection;
using System.Text;

namespace XamlSamples
{
    public class NamedColor
    {
        public string Name { get; private set; }
        public string FriendlyName { get; private set; }
        public Color Color { get; private set; }

        // Expose the Color fields as properties
        public float Red => Color.Red;
        public float Green => Color.Green;
        public float Blue => Color.Blue;

        public static IEnumerable<NamedColor> All { get; private set; }

        static NamedColor()
        {
            List<NamedColor> all = new List<NamedColor>();
            StringBuilder stringBuilder = new StringBuilder();

            // Loop through the public static fields of the Color structure.
            foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())
            {
                if (fieldInfo.IsPublic &&
                    fieldInfo.IsStatic &&
                    fieldInfo.FieldType == typeof(Color))
                {
                    // Convert the name to a friendly name.
                    string name = fieldInfo.Name;
                    stringBuilder.Clear();
                    int index = 0;

                    foreach (char ch in name)
                    {
                        if (index != 0 && Char.IsUpper(ch))
                        {
                            stringBuilder.Append(' ');
                        }
                        stringBuilder.Append(ch);
                        index++;
                    }

                    // Instantiate a NamedColor object.
                    NamedColor namedColor = new NamedColor
                    {
                        Name = name,
                        FriendlyName = stringBuilder.ToString(),
                        Color = (Color)fieldInfo.GetValue(null)
                    };

                    // Add it to the collection.
                    all.Add(namedColor);
                }
            }
            all.TrimExcess();
            All = all;
        }
    }
}

Cada NamedColor objeto tem Name e FriendlyName propriedades do tipo string, uma Color propriedade do tipo Color, e Red, Greene Blue propriedades. Além disso, o NamedColor construtor estático cria uma IEnumerable<NamedColor> coleção que contém NamedColor objetos correspondentes aos campos de tipo Color na Colors classe e a atribui à sua propriedade estática All pública.

A definição da propriedade estática NamedColor.All como ItemsSource de ListView pode ser obtida usando a extensão de marcação x:Static.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             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>

O resultado estabelece que os itens são do tipo XamlSamples.NamedColor:

Vinculação a uma coleção.

Para definir um modelo para os itens, o ItemTemplate deve ser definido como um DataTemplate que faz referência a um ViewCell. O ViewCell deve definir um layout de uma ou mais exibições para exibir cada item:

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

Observação

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

Neste exemplo, o Label elemento é definido como a View propriedade do ViewCell. As ViewCell.View tags não são necessárias porque a View propriedade é a propriedade content do ViewCell. Este XAML exibe a propriedade FriendlyName de cada objeto NamedColor.

Vinculação a uma coleção com um DataTemplate.

O modelo de item pode ser expandido para exibir mais informações e a cor real:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             x:Class="XamlSamples.ListViewDemoPage"
             Title="ListView Demo Page">
    <ContentPage.Resources>
        <x:Double x:Key="boxSize">50</x:Double>
        <x:Int32 x:Key="rowHeight">60</x:Int32>
        <local:FloatToIntConverter x:Key="intConverter" />
    </ContentPage.Resources>

    <ListView ItemsSource="{x:Static local:NamedColor.All}"
              RowHeight="{StaticResource rowHeight}">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:NamedColor">
                <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="14" />
                            <StackLayout Orientation="Horizontal"
                                         Spacing="0">
                                <Label Text="{Binding Red,
                                                      Converter={StaticResource intConverter},
                                                      ConverterParameter=255,
                                                      StringFormat='R={0:X2}'}" />                                
                                <Label Text="{Binding Green,
                                                      Converter={StaticResource intConverter},
                                                      ConverterParameter=255,
                                                      StringFormat=', G={0:X2}'}" />                                
                                <Label Text="{Binding Blue,
                                                      Converter={StaticResource intConverter},
                                                      ConverterParameter=255,
                                                      StringFormat=', B={0:X2}'}" />
                            </StackLayout>
                        </StackLayout>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

Conversores de valor de vinculação

O exemplo XAML anterior exibe as propriedades individuais Red, Greene Blue de cada NamedColor. Estas propriedades são do tipo float 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 números inteiros e, além disso, os float valores precisam ser multiplicados por 255.

Esse problema pode ser resolvido com um conversor de valor, também chamado de conversor de vinculaçã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 método ConvertBack é chamado para transferências de origem para destino nas vinculações OneWayToSource ou TwoWay.

using System.Globalization;

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

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

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

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

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

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

Observação

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

Uma vinculação refere-se a um conversor de vinculação com a propriedade Converter. 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 ligação verifica o parâmetro do conversor em busca de um valor válido float .

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

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

Três associações de dados fazem referência a essa única instância:

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

O modelo de item reproduz a cor, seu nome amigável e seus valores RGB:

Vinculação a uma coleção com um DataTemplate e um conversor.

O ListView pode lidar com alterações que ocorrem dinamicamente nos dados subjacentes, mas somente se você executar determinadas etapas. Se a coleção de itens atribuída à propriedade ItemsSource mudar durante o tempo de execução ListView, use a classe ObservableCollection<T> para esses itens. ObservableCollection<T> 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.

Próximos passos

As associações de dados fornecem um mecanismo poderoso para vincular propriedades entre dois objetos dentro de 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.