Parte 4. Conceptos básicos del enlace de datos

Descargar ejemploDescargar el ejemplo

Los enlaces de datos permiten vincular las propiedades de dos objetos para que un cambio en uno provoque un cambio en el otro. Se trata de una herramienta muy valiosa y, aunque los enlaces de datos se pueden definir completamente en el código, XAML proporciona accesos directos y comodidad. Por lo tanto, una de las extensiones de marcado más importantes de Xamarin.Forms es Binding.

Enlaces de datos

Los enlaces de datos conectan propiedades de dos objetos, denominados origen y destino. En el código, se requieren dos pasos: la BindingContext propiedad del objeto de destino debe establecerse en el objeto de origen y se SetBinding debe llamar al método (que se usa con frecuencia junto con la Binding clase ) en el objeto de destino para enlazar una propiedad de ese objeto a una propiedad del objeto de origen.

La propiedad de destino debe ser una propiedad enlazable, lo que significa que el objeto de destino debe derivar de BindableObject. La documentación en línea Xamarin.Forms indica qué propiedades son propiedades enlazables. Una propiedad de Label , como Text , está asociada a la propiedad TextPropertyenlazable .

En el marcado, también debe realizar los mismos dos pasos necesarios en el código, salvo que la Binding extensión de marcado tiene lugar la SetBinding llamada y la Binding clase .

Sin embargo, al definir enlaces de datos en XAML, hay varias maneras de establecer el BindingContext del objeto de destino. A veces se establece desde el archivo de código subyacente, a veces mediante una StaticResource extensión de marcado o x:Static y, a veces, como contenido de etiquetas de elemento de BindingContext propiedad.

Los enlaces se usan con más frecuencia para conectar los objetos visuales de un programa con un modelo de datos subyacente, normalmente en una realización de la arquitectura de la aplicación MVVM (Model-View-ViewModel), como se describe en la parte 5. Desde Enlaces de datos a MVVM, pero otros escenarios son posibles.

Enlaces de vista a vista

Puede definir enlaces de datos para vincular propiedades de dos vistas en la misma página. En este caso, se establece el BindingContext del objeto de destino mediante la x:Reference extensión de marcado.

Este es un archivo XAML que contiene una Slider y dos Label vistas, una de las cuales gira el Slider valor y otra que muestra el 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>

Slider contiene un x:Name atributo al que hacen referencia las dos Label vistas mediante la x:Reference extensión de marcado.

La x:Reference extensión de enlace define una propiedad denominada Name para establecer en el nombre del elemento al que se hace referencia, en este caso slider. Sin embargo, la ReferenceExtension clase que define la x:Reference extensión de marcado también define un ContentProperty atributo para Name, lo que significa que no es necesario explícitamente. Solo para la variedad, la primera x:Reference incluye "Name=" pero la segunda no:

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

La Binding propia extensión de marcado puede tener varias propiedades, al igual que la BindingBase clase y Binding . Para ContentPropertyBinding es Path, pero la parte "Path=" de la extensión de marcado se puede omitir si la ruta de acceso es el primer elemento de la extensión de Binding marcado. El primer ejemplo tiene "Path=", pero el segundo ejemplo lo omite:

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

Todas las propiedades pueden estar en una línea o separadas en varias líneas:

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

Haga lo que sea conveniente.

Observe la StringFormat propiedad en la segunda Binding extensión de marcado. En Xamarin.Forms, los enlaces no realizan ninguna conversión de tipos implícitas y, si necesita mostrar un objeto que no es de cadena como una cadena, debe proporcionar un convertidor de tipos o usar StringFormat. En segundo plano, el método estático String.Format se usa para implementar StringFormat. Esto podría ser un problema, ya que las especificaciones de formato de .NET implican llaves, que también se usan para delimitar extensiones de marcado. Esto crea un riesgo de confundir el analizador XAML. Para evitarlo, coloque toda la cadena de formato entre comillas simples:

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

Este es el programa en ejecución:

Enlaces de vista a vista

Modo de enlace

Una sola vista puede tener enlaces de datos en varias de sus propiedades. Sin embargo, cada vista solo puede tener un BindingContext, por lo que varios enlaces de datos en esa vista deben hacer referencia a todas las propiedades del mismo objeto.

La solución a este y otros problemas implica la Mode propiedad , que se establece en un miembro de la BindingMode enumeración:

  • Default
  • OneWay : los valores se transfieren del origen al destino.
  • OneWayToSource : los valores se transfieren del destino al origen.
  • TwoWay : los valores se transfieren de ambas maneras entre el origen y el destino.
  • OneTime: los datos van del origen al destino, pero solo cuando cambian.BindingContext

En el siguiente programa se muestra un uso común de los OneWayToSource modos de enlace y TwoWay . Cuatro Slider vistas están diseñadas para controlar las Scalepropiedades , Rotate, RotateXy RotateY de .Label En primer lugar, parece que estas cuatro propiedades de deben ser destinos de Label enlace de datos porque cada una se establece mediante .Slider Sin embargo, el BindingContext de Label solo puede ser un objeto y hay cuatro controles deslizantes diferentes.

Por ese motivo, todos los enlaces se establecen de maneras aparentemente anteriores: el BindingContext de cada uno de los cuatro controles deslizantes se establece Labelen y los enlaces se establecen en las Value propiedades de los controles deslizantes. Mediante el uso de los OneWayToSource modos y TwoWay , estas Value propiedades pueden establecer las propiedades de origen, que son las Scalepropiedades , Rotate, RotateXy RotateY de 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>

Los enlaces de tres de las Slider vistas son OneWayToSource, lo que significa que el Slider valor provoca un cambio en la propiedad de su BindingContext, que es el Label denominado label. Estas tres Slider vistas provocan cambios en las Rotatepropiedades , RotateXy RotateY de Label.

Sin embargo, el enlace de la Scale propiedad es TwoWay. Esto se debe a que la Scale propiedad tiene un valor predeterminado de 1 y el uso de un TwoWay enlace hace que el Slider valor inicial se establezca en 1 en lugar de 0. Si ese enlace fuera OneWayToSource, inicialmente la Scale propiedad se establecería en 0 del Slider valor predeterminado. no Label sería visible y esto podría causar cierta confusión al usuario.

Enlaces hacia atrás

Nota

La VisualElement clase también tiene ScaleX propiedades y ScaleY , que escalan en VisualElement el eje x y eje y respectivamente.

Enlaces y colecciones

Nada ilustra la eficacia de los enlaces de datos y XAML mejor que un objeto con ListViewplantilla.

ListView define una ItemsSource propiedad de tipo IEnumerabley muestra los elementos de esa colección. Estos elementos pueden ser objetos de cualquier tipo. De forma predeterminada, ListView usa el ToString método de cada elemento para mostrar ese elemento. A veces, esto es solo lo que desea, pero, en muchos casos, ToString devuelve solo el nombre de clase completo del objeto.

Sin embargo, los elementos de la ListView colección se pueden mostrar de cualquier manera que desee mediante el uso de una plantilla, lo que implica una clase que deriva de Cell. La plantilla se clona para cada elemento de , ListViewy los enlaces de datos que se han establecido en la plantilla se transfieren a los clones individuales.

Con mucha frecuencia, querrá crear una celda personalizada para estos elementos mediante la ViewCell clase . Este proceso es algo desordenado en el código, pero en XAML se vuelve muy sencillo.

Incluido en el proyecto XamlSamples es una clase denominada NamedColor. Cada NamedColor objeto tiene Name propiedades y FriendlyName de tipo string, y una Color propiedad de tipo Color. Además, NamedColor tiene 141 campos estáticos de solo lectura de tipo Color correspondientes a los colores definidos en la Xamarin.FormsColor clase . Un constructor estático crea una IEnumerable<NamedColor> colección que contiene NamedColor objetos correspondientes a estos campos estáticos y los asigna a su propiedad estática All pública.

Establecer la propiedad estática NamedColor.All en de ItemsSource un ListView es fácil de usar la x:Static extensión de marcado:

<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>

La presentación resultante establece que los elementos son verdaderamente de tipo XamlSamples.NamedColor:

Enlace a una colección

No es mucha información, pero es ListView desplazable y seleccionable.

Para definir una plantilla para los elementos, querrá dividir la ItemTemplate propiedad como un elemento de propiedad y establecerla en , DataTemplateque a continuación hace referencia a .ViewCell Para la View propiedad de ViewCell puede definir un diseño de una o varias vistas para mostrar cada elemento. Este es un ejemplo sencillo:

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

Nota

El origen de enlace para las celdas y los elementos secundarios de las celdas es la ListView.ItemsSource colección .

El Label elemento se establece en la View propiedad de ViewCell. (Las ViewCell.View etiquetas no son necesarias porque la View propiedad es la propiedad content de ViewCell). Este marcado muestra la FriendlyName propiedad de cada NamedColor objeto:

Enlace a una colección con un objeto DataTemplate

Mucho mejor. Ahora todo lo que se necesita es aducir la plantilla de elemento con más información y el color real. Para admitir esta plantilla, algunos valores y objetos se han definido en el diccionario de recursos de la 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 el uso de OnPlatform para definir el tamaño de y BoxView el alto de las ListView filas. Aunque los valores de todas las plataformas son los mismos, el marcado podría adaptarse fácilmente para otros valores para ajustar la pantalla.

Enlace de convertidores de valores

El archivo XAML de demostración de ListView anterior muestra las propiedades individuales R, Gy B de la Xamarin.FormsColor estructura. Estas propiedades son de tipo double y van de 0 a 1. Si desea mostrar los valores hexadecimales, no puede usar StringFormat simplemente con una especificación de formato "X2". Esto solo funciona para enteros y, además, los double valores deben multiplicarse por 255.

Este pequeño problema se resolvió con un convertidor de valores, también denominado convertidor de enlaces. Se trata de una clase que implementa la IValueConverter interfaz , lo que significa que tiene dos métodos denominados Convert y ConvertBack. Se Convert llama al método cuando se transfiere un valor de origen a destino; se llama al ConvertBack método para las transferencias de destino a origen en OneWayToSource o TwoWay enlaces:

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;
        }
    }
}

El ConvertBack método no desempeña un papel en este programa porque los enlaces son solo una manera de llegar de origen a destino.

Un enlace hace referencia a un convertidor de enlaces con la Converter propiedad . Un convertidor de enlaces también puede aceptar un parámetro especificado con la ConverterParameter propiedad . Para cierta versatilidad, así es como se especifica el multiplicador. El convertidor de enlaces comprueba el parámetro del convertidor para obtener un valor válido double .

Se crea una instancia del convertidor en el diccionario de recursos para que se pueda compartir entre varios enlaces:

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

Tres enlaces de datos hacen referencia a esta única instancia. Observe que la Binding extensión de marcado contiene una extensión de marcado incrustada StaticResource :

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

Este es el resultado:

Enlace a una colección con datatemplate y convertidores

ListView es bastante sofisticado en el control de los cambios que pueden producirse dinámicamente en los datos subyacentes, pero solo si realiza ciertos pasos. Si la colección de elementos asignados a la propiedad de los cambios durante el ItemsSourceListView tiempo de ejecución( es decir, si se pueden agregar o quitar elementos de la colección), use una ObservableCollection clase para estos elementos. ObservableCollection implementa la INotifyCollectionChanged interfaz e ListView instalará un controlador para el CollectionChanged evento.

Si las propiedades de los propios elementos cambian durante el tiempo de ejecución, los elementos de la colección deben implementar la INotifyPropertyChanged interfaz y indicar los cambios en los valores de propiedad mediante el PropertyChanged evento . Esto se muestra en la siguiente parte de esta serie, Parte 5. Del enlace de datos a MVVM.

Resumen

Los enlaces de datos proporcionan un mecanismo eficaz para vincular propiedades entre dos objetos dentro de una página, o entre objetos visuales y datos subyacentes. Pero cuando la aplicación comienza a trabajar con orígenes de datos, un patrón de arquitectura de aplicación popular comienza a surgir como paradigma útil. Esto se trata en la parte 5. Desde Enlaces de datos a MVVM.