[Windows 10] x:Bind, bindings compilados

Introducción

Data binding es un mecanismo mediante el cual
podemos enlazar los elementos de la interfaz de usuario con los objetos
que contienen la información a mostrar. Cuando realizamos data binding,
creamos una dependencia entre el valor de una propiedad llamada target con el valor de otra propiedad llamada source. Donde normalmente, la propiedad target recibirá el valor de la propiedad source.

Es el mecanismo base que nos permite utilizar el patrón MVVM en nuestras Apps móviles logrando:

  • Nos permite dividir el trabajo de manera muy sencilla (diseñadores – desarrolladores)
  • El mantenimiento es más sencillo.
  • Permite realizar Test a nuestro código.
  • Permite una más fácil reutilización de código.

Sin embargo, además de toda la potencia mencionada teníamos ciertas
limitaciones. Los errores de Binding no se producían en tiempo de
compilación de la App además de tener diferentes mejoras relacionadas
con el rendimiento. Limitaciones existentes hasta ahora…

Con la llegada de Windows 1o tenemos la posibilidad de crear bindings compilados en lugar de los bindings clásicos.

¿Cómo se usan?, ¿que aportan?, ¿cúando usarlos?

A todas esas preguntas daremos respuesta en este artículo.

x:Bind

x:Bind es una nueva síntaxis en XAML que cubre un objetivo similar a
Binding. Permite crear un enlace a datos pero con significativas
diferencias. Mientras que con Binding se utiliza reflexión en tiempo de
ejecución para resolver el enlace a datos, con x:Bind se realiza una
validación en tiempo de ejecución ya que son fuertemente tipados y compilados. Además, ofrece potentes mejoras en el rendimiento.

Crearemos un nuevo proyecto UAP:

Nueva App UAP

Nuestro objetivo en este primer ejemplo será crear 1600 Borders
bindeados a colores utilizando Bindings clásicos y Bindings compilados.
De esta forma podremos realizar una interesante comparativa en los
tiempos necesarios por cada forma para realizar el enlace a datos así
como otros detalles interesantes como el consumo de memoria, etc.

Comenzamos creando un diccionario de recursos donde definiremos los distintos colores:

<SolidColorBrush x:Key="BackgroundA" Color="Red" />
<SolidColorBrush x:Key="BackgroundB" Color="Blue" />
<SolidColorBrush x:Key="BackgroundC" Color="Green" />
<SolidColorBrush x:Key="BackgroundD" Color="Yellow" />

A continuación crearemos dos vistas nuevas. En una de ellas crearemos cientos de Borders utilizando enlace de datos clásico:

<Border Height="30" Width="30" Background="{Binding BackgroundA}" />
<Border Height="30" Width="30" Background="{Binding BackgroundB}" />
<Border Height="30" Width="30" Background="{Binding BackgroundC}" />
<Border Height="30" Width="30" Background="{Binding BackgroundD}" />

Mientras que en la otra utilizaremos x:Bind:

<Border Height="30" Width="30" Background="{x:Bind BackgroundA}" />
<Border Height="30" Width="30" Background="{x:Bind BackgroundB}" />
<Border Height="30" Width="30" Background="{x:Bind BackgroundC}" />
<Border Height="30" Width="30" Background="{x:Bind BackgroundD}" />

Para utilizar bindings compilados reemplazaremos {Binding} por {x:Bind}.
Los bindings compilados están fuertemente tipados por lo que es
necesario indicar el tipo del contexto siendo por defecto la página o el
control personal en si.

NOTA: El modo de binding utilizado por defecto en bindings compilados es OneTime.

En la vista principal tendremos un par de botones que nos permitan
instanciar y medir el tiempo necesario para ello en cada caso:

var date = DateTime.Now;
var binding = new XBind();
string diff = string.Empty;
binding.Loaded += (s, args) =>
{
     diff = (DateTime.Now - date).ToString();
     Diff.Text = diff;
};
 
Content.Children.Add(binding);

Si ejecutamos la App y pulsamos el botón “Binding”:

Bindings clásicos

Si utilizamos x:Bind:

x:Bind

Si comparamos los tiempos vemos que… se reduce a practicamente la mitad!

Además, analizando consumo de memoria y CPU vemos la siguiente comparativa.

Uso de CPU utilizando enlace a datos clásico:

Uso de CPU en Bindings clásicos

Uso de CPU utilizando bindings compilados:

Uso de CPU en binding compilado

También se reduce el consumo de memoria en comparación con Bindings clásicos:

Comparativa de consumo de memoria entre Bindings

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

Utilizando x:Bind

Vista las visibles mejoras a nivel de rendimiento, vamos a crear una App “normal” donde veamos su uso.

Crearemos un nuevo proyecto UAP:

Nueva App UAP

Nueva App UAP

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo.

En esta ocasión, nuestro objetivo sera crear un listado de casas
donde utilizaremos x:Bind en la plantilla que representará cada elemento
de la lista.

Comenzamos creando la entidad casa dentro de la carpeta Models:

public class House
{
     public string Place { get; set; }
     public string Price { get; set; }
     public string Photo { get; set; }
}

Nuestra interfaz sera muy simple en esta ocasión contando con un sencillo ListView:

<ListView
     ItemsSource="{Binding Houses}" />

El control tiene la fuente de datos enlazada a una propiedad de la ViewModel:

private ObservableCollection<House> _houses;
 
public ObservableCollection<House> Houses
{
     get
     {
          if (_houses == null)
               LoadHouses();
 
          return _houses;
     }
}

Cargaremos el listado de casas con un método creando datos falsos en local de manera aleatoria:

private void LoadHouses()
{
     _houses = new ObservableCollection<House>();
     Random random = new Random();
     for (int i = 0; i < 100; i++)
     {
          _houses.Add(new House
          {
               Place = Places[random.Next(0, Places.Count)],
               Photo = string.Format("ms-appx:///Assets/{0}.png", random.Next(1, 4)),
               Price = string.Format("${0}", random.Next(10000, 100000).ToString())
          });
     }
}

Y llegamos a la parte más importante, la definición del template de cada casa:

<DataTemplate x:Key="HouseTemplate" x:DataType="model:House">
     <Grid Width="200"
           Height="80">
          <Grid.ColumnDefinitions>
               <ColumnDefinition Width="75" />
               <ColumnDefinition Width="*" />
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
               <RowDefinition Height="Auto" />
               <RowDefinition Height="Auto" />
           </Grid.RowDefinitions>
           <Image Grid.RowSpan="2"
              Source="{x:Bind Photo}"
              MaxWidth="70"
              MaxHeight="70" />
           <TextBlock Text="{x:Bind Place}"    
                  Grid.Column="1"
                  FontSize="18"/>
           <TextBlock Text="{x:Bind Price}"  
                  Grid.Column="1"  
                  Grid.Row="1"
                  FontSize="12" />
     </Grid>
</DataTemplate>

Utilizamos x:Bind para enlazar cada elemento visual de la plantilla a
la propiedad deseada. Importante resaltar además de compilados, son
fuertemente tipados. Es obligatorio para no tener errores de compilación
indicar el tipo de los datos a los que accedemos por enlace a datos.
Esto lo realizamos utilizando x:DataType. En nuestro ejemplo, la entidad House.

Nuestro ListView quedara:

<ListView
     ItemsSource="{Binding Houses}"
     ItemTemplate="{StaticResource HouseTemplate}" />

Ejecutando la App:

DataTemplate utilizando x:Bind

 

Podéis descargar el ejemplo realizado a continuación:

También podéis acceder al código fuente directamente en GitHub:

Ver GitHub

¿Cuándo utilizar Bindings compilados?

Lo visto hasta ahora nos indica que:

  • Tenemos la posibilidad de tener bindings compilados obteniendo errores en tiempo de compilación.
  • Son fuertemente tipados por lo que debemos indicar el tipo de la información.
  • Obtenemos mejoras en el rendimiento tanto en consumo de CPU como de memoria.

Por lo tanto, ¿lo usamos siempre?

La respuesta corta es no. Entrando en profundidad:

  • Los bindings compilados, en ocasiones,  tienen un comportamiento
    diferente al de los bindings clásicos existiendo situaciones no válidas
    para los primeros.
  • Los bindings compilados como indicamos al nombrarlos se compilan
    permitiéndonos obtener errores en tiempo de compilación pero tambien nos
    aporta limitaciones. No podemos crear bindings compilados dinámicamente
    (añadir o quitar bindings en runtime).
  • Con los bindings clásicos podemos crear un mismo template para
    entidades diferentes siempre y cuando el nombre de las propiedades
    coincida. Con los bindings compilados como hemos visto, estan
    fuertemente tipados y no podemos realizar lo mismo.

Por lo tanto, en nuestros desarrollos entran en juego los bindings
compilados pero no sustituyen en abosluto a los bindings clásicos.
Dependiendo de la situación utilizaremos unos u otros en consecuencia.

Más información

Deja un comentario

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