Uso del enlace de datos en Xamarin.Forms

La mayoría de las aplicaciones están controladas por datos. Podrían ser datos generados por el usuario o descargados de Internet. Es posible que los datos se compilen directamente en la aplicación. Incluso los juegos dependen en gran medida de los datos para puntuaciones, marcadores, tokens de cuenta, etc. Cuando los datos cambian, asegúrese de que lo que se muestra en la interfaz de usuario permanece sincronizado con lo que se almacena en el código subyacente.

Desventajas de usar código para sincronizar los datos y la interfaz de usuario

Normalmente, las aplicaciones tienen dos tareas de sincronización:

  • Llegan nuevos datos desde la web. Se actualiza la interfaz de usuario.
  • El usuario escribe valores en la interfaz de usuario. Se actualizan los objetos de modelo de datos.

Para estos dos trabajos se puede usar código. En la imagen siguiente se muestra la interfaz de usuario de una aplicación de "lista de tareas pendientes" y el código que se usa para transferir datos entre la interfaz de usuario y los objetos del modelo de datos. El objeto task es el modelo de datos. Name, Notes e IsDone son los elementos de la interfaz de usuario que muestran los datos. Observe que hay dos bloques de código: uno que carga datos en la interfaz de usuario y otro que reacciona cuando el usuario cambia los datos.

Imagen que muestra un ejemplo de una aplicación de seguimiento de tareas, con código que se usa para mantener sincronizados los objetos de la interfaz de usuario y del modelo de datos y la captura de pantalla de la aplicación de iOS resultante.

El código asigna la propiedad Text de dos etiquetas y la propiedad IsToggled de un botón de alternancia. Luego la interfaz de usuario se modifica mediante los controladores de eventos TextChanged y Toggled.

Este enfoque puede funcionar para aplicaciones pequeñas. Pero puede ser difícil de lograr a medida que la aplicación aumente de tamaño y las interacciones sean más complejas. Por ejemplo, podría tener varios controles, que manipulan los mismos datos. O bien, puede haber eventos en un control que afecten al estado de otros controles. También resulta difícil escribir pruebas unitarias cuando toda la lógica se encuentra en el archivo de código subyacente del elemento Page.

¿Qué son los enlaces de datos?

Un enlace une dos propiedades. Una propiedad está en la interfaz de usuario y la otra en el objeto de modelo de datos. Si cambia el valor de una de las propiedades, el objeto de enlace puede actualizar la otra. En otras palabras, los enlaces son objetos intermedios que sincronizan los datos y la interfaz de usuario. Se usan los términos origen y destino para identificar los dos objetos implicados.

Las reglas para el origen y el destino son diferentes:

  • Origen: puede ser un objeto de cualquier tipo. En la práctica, normalmente se usa como origen un objeto de datos de C#. Tiene que identificar la propiedad en ese objeto de origen para participar en el enlace. La propiedad se identifica al establecer el elemento Path en el enlace.

  • Destino: el objeto de destino se debe derivar de BindableObject. La propiedad de destino se debe implementar mediante una propiedad especial denominada BindableProperty. En última instancia, todos los controles de Xamarin.Forms se derivan de BindableObject (a través de View). Sus propiedades son principalmente de tipo BindableProperties. Esta arquitectura hace que todos los controles de Xamarin.Forms sean destinos de enlace adecuados.

En la ilustración siguiente se resumen las reglas para el origen y el destino de un enlace.

Ilustración que muestra un enlace como un intermediario entre una propiedad de objeto de origen y una propiedad enlazable de objeto de destino.

Creación de enlaces de datos en XAML

Es muy habitual crear los enlaces en XAML. Hay un par de razones para preferir XAML. En primer lugar, la mayoría de los usuarios consideran que los enlaces forman parte del código de su interfaz de usuario porque obtienen datos para que la interfaz de usuario los muestre. En segundo lugar, hay una extensión de marcado denominada Binding que facilita esta tarea.

Se verá un enlace de ejemplo creado en XAML mediante la extensión de marcado {Binding}. Enlaza la propiedad Todo.Title del origen a la propiedad Entry.Text del control de interfaz de usuario.

<Entry Text="{Binding Title,
              Source={StaticResource getMilk}}" />

:::image type="complex" source="../media/BindingInXaml.png" alt-text="Imagen que muestra un ejemplo de una aplicación de seguimiento de tareas, con código que se usa para mantener sincronizados los objetos de la interfaz de usuario y del modelo de datos y la captura de pantalla de la aplicación de iOS resultante." apunta a la sintaxis de StaticResource. :::image-end:::

En el origen:

  • El objeto está en el diccionario de recursos de la página con un elemento Key de getMilk.
  • El valor de Path es Title.

Para el destino:

  • El objeto es el control Entry.
  • La propiedad es Text.

Cuando se carga este archivo XAML, la extensión de marcado {Binding} crea un enlace entre Todo y Entry. El objeto de enlace lee Todo.Title y carga el valor en Entry.Text.

Creación de enlaces de datos en el código

Puede crear enlaces en C#. Esta técnica puede ser útil si genera la interfaz de usuario de forma dinámica. En la práctica, es poco frecuente porque la estructura de la mayoría de las interfaces de usuario es estática y la sintaxis de los enlaces basados en código es detallada. Se verá cómo hacerlo en caso de que lo necesite alguna vez.

// Create Source
ToDo task = new ToDo() { Title = "Pickup some Milk", ... };

// Create target
Entry Name = new Entry();

// Create binding, identifying Source and Path
Binding nameBinding = new Binding()
nameBinding.Source = task;
nameBinding.Path = "Title";

// Set Binding, identifying Target object and Target property
Name.SetBinding(Entry.TextProperty, nameBinding);

Hay un par de aspectos interesantes que conviene destacar.

  • La propiedad de destino se identifica mediante el nombre de la propiedad enlazable. En este caso, se usa Entry.TextProperty. Las propiedades enlazables se definen mediante miembros estáticos que usan la convención de nomenclatura de un sufijo "Property". La propiedad de instancia denominada Text es simplemente un contenedor en torno a la propiedad enlazable subyacente.

  • Path es de tipo cadena. En el ejemplo, la ruta de acceso se establece en un nombre de propiedad simple Title. La ruta de acceso permite realizar tareas más eficaces. Por ejemplo, puede usar propiedades anidadas como Title.Length. Incluso puede usar indizadores para los valores enumerables, como Title[0]. Esta sintaxis de Path enriquecida también está disponible para los enlaces de XAML.

Uso del mismo origen para varios enlaces

Hasta ahora se ha descrito un enlace a la vez. Pero en el caso más común, habrá varios controles que extraen datos del mismo objeto de origen. Por ejemplo, en la imagen siguiente se muestra parte de la interfaz de usuario de una aplicación de tareas pendientes y el objeto de datos que se usa como origen de enlace. Observe cómo esta página muestra tres propiedades de un único objeto Todo.

Imagen que muestra un ejemplo de una aplicación de seguimiento de tareas, con código que se usa para mantener sincronizados los objetos de la interfaz de usuario y del modelo de datos y la captura de pantalla de la aplicación de iOS resultante.

El código es una clase Todo con las propiedades Title, Notes y Completed. La propiedad Title enlaza a una entrada Nombre, la propiedad Notes enlaza a una entrada Notas y la propiedad Completed enlaza a un botón de alternancia.

Esta situación es tan común que los enlaces tienen un caso especial para controlarla. La clase BindableObject define una propiedad denominada BindingContext. Todas las páginas, paneles de diseño y controles de Xamarin.Forms heredan de BindableObject, por lo que todos tienen esta propiedad.

Cuando se crea un enlace, el valor Source es opcional. Un enlace sin un valor para Source busca de forma automática propiedades BindingContext que no sean NULL en el árbol visual:

  1. La búsqueda comienza con el propio objeto de destino. Si esa propiedad BindingContext es distinta de NULL, la búsqueda se detiene y se aplica Path a ese objeto. Si la propiedad BindingContext es NULL, la búsqueda continúa.

  2. Después, la búsqueda pasa al elemento primario del objeto de destino. Normalmente, este objeto es un panel de diseño. Aquí se produce el mismo proceso. Si la propiedad BindingContext del panel es distinta de NULL, la búsqueda se detiene. Si es NULL, la búsqueda continúa al elemento primario del panel.

  3. Este proceso continúa hasta que llega a la página. La búsqueda finaliza mediante la comprobación de la propiedad BindingContext del elemento Page.

Es habitual establecer la propiedad BindingContext en el nivel de la página. Los enlaces XAML solo contienen la ruta de acceso de origen, lo que ayuda a reducir el tamaño del XAML y a que sea más legible.

// In code-behind we set Page.BindingContext
BindingContext = new Todo() { Title = "Pickup some milk", Notes = "Stop at the Grocery Store!", Completed = true }
<!-- In XAML our bindings do not need to set Source -->
<StackLayout Padding="20" Spacing="20">
    <Entry Text="{Binding Title}" />
    <Entry Text="{Binding Notes}" />
    <Switch IsToggled="{Binding Completed}" />
</StackLayout>

Hay una última característica interesante que conviene mencionar. Los enlaces observan los cambios en la referencia de objetos de su origen. Esta característica funciona incluso con enlaces que usan BindingContext como origen. Si se reasigna Source o BindingContext a otro objeto, los enlaces obtienen los datos del origen nuevo y actualizan su destino.

Comprobación de conocimientos

1.

¿Qué afirmación es cierta sobre el objeto de origen de un enlace de Xamarin.Forms?

2.

¿Qué afirmación es cierta sobre la propiedad de destino de un enlace de Xamarin.Forms?

3.

Si en todos los enlaces de los controles de un elemento StackLayout se usa el mismo objeto de origen, ¿cuál es la estrategia más segura para establecer el objeto de origen una sola vez?