[Xamarin.Forms] Extensiones de marcado

Introducción

Xamarin.Forms añade una capa de abstracción en la capa de UI que nos permite definir la misma una única vez para todas las plataformas. Podemos definir esta interfaz con código C# o XAML.

A la hora de trabajar con la interfaz en XAML con casi toda seguridad utilizarás alguna extensión de marcado.

Extensiones de marcado

Las extensiones de marcado son una forma de obtener un valor que no sea específico de tipo primitivo o un objeto XAML. Mediante la apertura y cierre de llaves, se define en su interior lo que se conoce como extensión de marcado.

Existen extensiones de marcados muy usadas y conocidas como Binding o StaticResource. Pero… ¿conoces todas las extensiones disponibles y su uso?.

En este artículo, vamos a conocer todas las extensiones básicas existentes.

Binding

Estamos ante la expresión de marcado más utilizada. Cuando el código XAML se encuentra con un literal entre llaves, lo interpreta y no se limita a mostrarlo como si fuera un simple texto. En este caso hace una evaluación encontrando la palabra reservada Binding, lo que indica que debe enlazar el valor de una propiedad. Pero…¿cómo sabe dónde se encuentra la propiedad?, todos los elementos que componen la interfaz de usuario descienden de una importante jerarquía de clases base que otorgan funcionalidad vital.

Contamos con la propiedad BindingContext que actúa como contexto de datos del elemento. Si la propiedad fuese nula en el elemento, se buscaría de forma ascendente en la jerarquía de elementos. Es decir, si la propiedad BindingContext del elemento visual es nula, buscará en el elemento padre inmediato, etc.

Veamos un ejemplo.

public class Person
{
     public string Name { get; set; }
     public string SurName { get; set; }
}

Establecemos el BindingContext:

BindingContext = person;

A la hora de utilizar el Binding:

<Label Text={Binding Name}" />

En un enlace a datos, la palabra reservada Mode indica la forma en la que se comporta el mismo. En Xamarin.Forms contamos con los siguientes modos:

  • OneWay: es el valor por defecto. Indica que el enlace se realiza en un único sentido, de lectura. Aunque el valor del elemento visual cambie, no viajará hasta el  ViewModel.
  • OneWayToSource: en un único sentido generalmente de la View al ViewModel.
  • TwoWay: en este caso el enlace es bidireccional. La View toma el valor del ViewModel, pero si lo editamos en la vista, el cambio se envía de vuelta al ViewModel.
  • Default: el modo utilizado por defecto, que como hemos visto es OneWay.
<Entry Text={Binding Username, Mode=TwoWay}" />

Por otro lado, en ocasiones, lo que se desea mostrar en la interfaz de usuario no equivale al valor enlazado. Podríamos extender modelos añadiendo más propiedades para este propósito, pero no es lo ideal. Para solventar este problema contamos con las propiedades de formateo de texto.

<Label Text="{Binding Count, StringFormat='Number of Records = {0:N}'}"/>

Veamos otro ejemplo:

<Label Text="{Binding Date, StringFormat='{0:dd MM yyyy}'}" />

Converters

En ocasiones, el valor enlazado no es el deseado. Por ejemplo, tenemos enlazado un valor de tipo DateTime pero deseamos mostrar una cadena de texto al usuario con un formato de fecha aplicado. Justo para este objetivo, tenemos los TypeConverters.

En lugar de extender nuestros modelos con más y más valores, los Converters nos permiten transformar unos valores a otros directamente desde XAML.

<Label Text="{Binding Date}" />

En este caso veríamos el resultado de lanzar el método ToString del DateTime. Podemos crear un Converter:

public class DatetimeToStringConverter : IValueConverter
{
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
          if (value == null)
               return string.Empty;

          var datetime = (DateTime)value;
          return datetime.ToLocalTime().ToString("g");
     }

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
          throw new NotImplementedException(); 
     }
}

El Converter es una clase que herede de la interfaz IValueConverter, se implementa el método Convert y ConvertBack. Para utilizar el converter se debe definir primero como recurso:

<ResourceDictionary>
 <local:DatetimeToStringConverter x:Key="DateTimeConverter" />
</ResourceDictionary>

Y posteriormente, utilizar la palabra reservada Converter para acceder al mismo:

<Label Text="{Binding Date, Converter={StaticResource DateTimeConverter}}" />

NOTA: Se suelen utilizar los Converters para transformaciones más complejas.

TemplateBinding

Con la versión 2.1.0 de Xamarin.Forms se introdujo el concepto de Control Templates y también el de TemplateBinding. Un TemplateBinding es similar a un Binding, excepto que el origen de un TemplateBinding siempre se establece automáticamente en el padre de la vista de destino que posee la plantilla de control.

<ControlTemplate x:Key="PageTemplate">
     <StackLayout>
          <Label Text="{TemplateBinding BindingContext.HeaderText}" /> 
          <ContentPresenter />
     </StackLayout>
</ControlTemplate>

StaticResource

En cada elemento visual podemos definir un conjunto de recursos. Utilizamos ResourceDictionary para a gestión de recursos. Con StaticResource podemos acceder a objetos estáticos definidos como recursos y disponibles en tiempo de compilación.

<ContentPage.Resources>
 <ResourceDictionary>

     <Style x:Key="ErrorLabelStyle" TargetType="Label">
          <Setter Property="TextColor" Value="Red" />
     </Style>

</ResourceDictionary>
</ContentPage.Resources>

<Label Text="Hello!" Style="{StaticResource ErrorLabelStyle}" />

DynamicResource

Esta extensión de marcado es sumamente similar a la anterior pero cuenta con una gran diferencia. DynamicResource permite acceder a recursos añadidos en tiempo de ejecución. Es decir, si un recurso es eliminado / añadido en un diccionario de recursos en tiempo de ejecución, DynamicResource es tu extensión.

var errorLabelStyle = new Style(typeof(Label));

errorLabelStyle.Setters.Add(new Setter() 
{ 
     Property = Label.BackgroundColorProperty, Value = Color.Red 
});

this.Resources.Add(errorLabelStyle);

Tras añadir dinámicament el tiempo de ejecución el estilo, accedemos al mismo:

<Label Text="Hello!" Style="{DynamicResource ErrorLabelStyle}" />

Otras extensiones de marcado

Existen estensiones de marcado intrínsecas de XAML y soportadas en archivos XAML de Xamarin.Forms.

x:Array

Se pueden definir arrays directamente en XAML gracias a esta extensión de marcado.

<x:Array x:Key="StringArray" Type="{x:Type x:String}">
     <x:String>Hello</x:String>
     <x:String>World</x:String>
</x:Array>

x:Null

Extensión de marcado que permite establecer valor nulo. Si una propiedad cuenta con soporte a valores no nulos y se desea establecer por defecto a nulo, {x:Null} es la extensión de marcado idónea.

x:Reference

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

<StackLayout>
     <Slider 
          x:Name="slider"
          Maximum="100" />
     <Label 
          BindingContext="{x:Reference slider}"
          Text="{Binding Value,
          StringFormat='The angle is {0:F0} degrees'}" />
 </StackLayout>

x:Static

A pesar de lo que pueda dar a pensar, x:Static y StaticResource NO son iguales. Mientras que StaticResource nos permite acceder a un objeto definido en un diccionario de recursos, x:Static nos permite el acceso a una propiedad o constante estática.

<Label 
     Text="Awesome!"
     TextColor="{x:Static Color.Blue}" />

NOTA: Esta extensión de marcado es más potente de lo que pueda parecer. También puedes acceder a campos o propiedades estáticas de tu propio código. Si se cuenta con una clase estática con constantes, se podrían utilizar.

x:Type

Permite establecer el tipo del objeto utilizando {x:Type Class}.

Extensiones de marcado personalizadas

Llegamos hasta aquí!. Cuando se comienza a desarrollar en XAML algunas son muy utilizadas y bien conocidas pero no así con todas. Xamarin.Forms también permite crear nuevas extensiones de marcado personalizadas aunque es algo que veremos en un próximo artículo.

Hasta la próxima!

Más información

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *