[Evento online] Trucos y consejos rendimiento Xamarin.Forms

Introducción

Recientemente, en SVQXDG, tuvimos un interesante evento Xamarin relacionado con el rendimiento en Xamarin.Forms. Tras recibir feedback por diferentes redes sociales, meetup o vía correo, ante el interés en diferentes partes geográficas hemos decidido…repetir el evento online!

El evento

¿Sabes el ciclo de vida de un Layout?, ¿qué opciones de Layout son más óptimas?, ¿cómo afectan los Bindings al rendimiento y como tratarlos?, ¿rendimiento en listados?, ¿fast renderers?. A todas esas preguntas y a otras tantas, intentaremos dar solución en esta sesión online!.

El evento será el próximo Sábado, 17 de Junio a las 12:00h (GMT+1).

¿Te apuntas?

Más información

[Material] Rendimiento en Xamarin.Forms

Introducción

En Xamarin.Forms, ¿sabes el ciclo de vida de un Layout?, ¿qué opciones de Layout son más óptimas?, ¿cómo afectan los Bindings al rendimiento y como tratarlos?, ¿rendimiento en listados?, ¿fast renderers?. A todas esas preguntas y a otras tantas, intentamos dar respuesta en el último evento de SVQXDG celebrado el pasado 24 de Mayo.

El material

La presentación:

En cuanto a las demos técnicas realizadas, las tenéis disponible en GitHub:

Ver GitHubGracias a todos los asistentes al evento. En esta ocasión me ha sorprendido la enorme cantidad de feedback y peticiones relacionados con esta sesión. Pronto se anunciará su repetición en esta ocasión online!

Nos vemos en la próxima!

Más información

[Evento SVQXDG] Rendimiento en Xamarin.Forms

El evento

Continuamos desde SVQXDG con otro nuevo evento. En esta ocasión un tema muy tratado en diferentes quedadas y mencionado en eventos pero nunca hemos profundizado hasta ahora, el rendimiento en Xamarin.Forms.

¿Sabes el ciclo de vida de un Layout?, ¿qué opciones de Layout son más óptimas?, ¿cómo afectan los Bindings al rendimiento y como tratarlos?, ¿rendimiento en listados?, ¿fast renderers?. A todas esas preguntas y a otras tantas, intentaremos dar solución en esta sesión!.

La fecha

El evento tendrá lugar el próximo Miércoles, 24 de Mayo de 19:00h a 20:30h. Tendremos una única sesión técnica de 90 minutos de duración. Además contaremos con algún detalle entre los asistentes.

¿Te apuntas?

El lugar

El evento se celebrará en la ETS de Ingeniería Informática. Dirección detallada:

E.T.S. Ingeniería Informática – Universidad de Sevilla, Aula B1.32
Av. Reina Mercedes s/n
Sevilla Se 41012

ETS de Ingeniería Informática

Más información

[Xamarin.Forms] Optimizando listados con estrategias de cacheo

JumpListXamarin.Forms 2.0

Con la llegada de Xamarin 4 han aterrizado una gran cantidad de diversas novedades entre las que destaca la versión 2.0 de Xamarin Forms. Entre las novedades principales contamos con:

  • Soporte a UWP.
  • Compilación de XAML (XAMLC).
  • Optimización en Layouts.
  • Optimización de listados. Incluidas nuevas estrategias de cacheo.
  • Corrección de bugs.

Optimizando listados

Entre el listado de controles fundamentales de cualquier aplicación, junto a textos, cajas de textos y botones, destacaríamos los listados. Es un control muy habitual en la inmensa mayoría de aplicaciones.

Como hemos visto en el listado de novedades de Xamarin.Forms 2.0, se han introducido mejoras en el rendimiento del control listView incluyendo nuevas estrategias de cacheo.

Estrategias de cacheo

A menudo, en listados debemos soportar grandes cantidades de elementos muy superiores a lo que se puede mostrar en pantalla e incluso a lo que se puede alcanzar con un ligero scroll. Esto impacta negativamente en el rendimiento. Tanto en iOS como en Android contamos con mecanismos de reciclado de celdas que permiten solucionar o mitigar este problema. La técnica consiste en contar solo en memoria con una pequeña cantidad de celdas en memoria de modo que se van reciclando y reutilizando elementos según el usuario realiza scroll.

Con la llegada de Xamarin.Forms 2.0 podemos aprovechar estas características nativas desde Forms. El control ListView recibe una nueva propiedad llamada CachingStrategy para esteblecer el tipo de cacheo realizado. Soporta uno de los siguientes valores:

  • RetainElement: Comportamiento por defecto. Genera una celda diferente por cada elemento del listado. Es recomendable su uso en caso de gran cantidad de cambios en el contexto. Sin embargo, hay que tener en cuenta que el código de inicialización de cada celda se ejecutará por cada celda pudiendo afectar al rendimiento.
  • RecycleElement: Esta opción toma ventajas de los mecanismos nativos de iOS y Android para el reciclado de celdas. Recomendable su uso cuando las celdas no tienen grandes cambios de contexto y su layout es similar. Reduce el consumo de memoria y la rapidez de uso.

Para realizar pruebas de rendimiento, crearemos un sencillo ejemplo con un listado con un número elevado de elementos (monos). En nuestra ViewModel creamos un listado:

public ObservableCollection<Monkey> Monkeys
{
     get { return _monkeys; }
     set
     {
          _monkeys = value;
          RaisePropertyChanged();
     }
}

Creamos un listado aleatorio de 1000 elementos:

_count = 1;
Random random = new Random();
for (int i = 0; i < 1000; i++)
{
     Monkeys.Insert(0,
          new Monkey
          {
               Name = string.Format("Monkey {0}", _count),
               Location = Countries[random.Next(0, Countries.Count)],
               Photo = Images[random.Next(0, Images.Count)]
          });
     _count++;
}

En la interfaz, utilizamos el control ListView utilizando el reciclado de celdas:

<ListView
     ItemsSource="{Binding Monkeys}"
     CachingStrategy="RecycleElement">
     <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <StackLayout Orientation="Vertical">
            <StackLayout Orientation="Horizontal">
              <Image Source="{Binding Photo}"
                     Aspect="AspectFill"
                     WidthRequest="100"/>
              <Label Text="{Binding Name}"
                     VerticalOptions="Center"/>
              <Label Text="{Binding Location}"
                     HorizontalOptions="EndAndExpand"
                     VerticalOptions="Center"/>
            </StackLayout>
          </StackLayout>
        </ViewCell>
      </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

El resultado:

 

Reciclando celdas

Reciclando celdas

Tenéis el código fuente disponible en GitHub:

Ver GitHub

Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

[Windows 10] Novedades y consejos sobre rendimiento

Branch-EngineeringIntroducción

Además de cuidar detalles como la funcionalidad o la apariencia visual de nuestra aplicación, nuestra aplicación debe funcionar correctamente bajo todas las condiciones en todos los dispositivos para la que sea lanzada.

Factores como el consumo de memoria, CPU o la gestión de tiempos, a veces cuestan y no se tienen en cuenta hasta que salta el problema, es decir, tarde.

¿Es importante el rendimiento?

Las críticas de la tienda así como la posibilidad de comunicación directa con los usuarios es una vía inmejorable para realizar una mejora continua de la misma. Las críticas habituales suelen venir en un alto porcentaje de errores (bugs) que no permiten realizar acciones contempladas en la aplicación y también muchas de ellas, por rendimiento.

 

Motivos de críticas negativas

Motivos de críticas negativas

Que la aplicación se “congele”, respuestas lentas, consumos muy elevados de batería, que provoquen un sobrecalentamiento del dispositivo son algunas causas habituales de opiniones como las siguientes:

Ejemplos de críticas negativas

Ejemplos de críticas negativas

Por lo tanto, debemos tener en cuenta en el propio desarrollo factores de rendimiento de igual forma que prestamos atención a la correcta visualización de la interfaz en distintas condiciones por ejemplo.

Mejoras de rendimiento en UWP

De entrada, buenas noticias. Se han incluido en Windows 10 mejoras como:

  • Rendimiento aumentado en Listview.
  • Mejoras en redimiento en ScrollViewer.
  • Interoperatibilidad XAML/DX.
  • Casting de elementos visuales.
  • Gestión Bitmap Source.
  • Etc.

Gracias a todas estas mejoras conseguimos sin cambios en nuestra forma de trabajar o en el código, mejoras en el consumo de CPU y de memoria.

Mejoras rendimiento UWP

Mejoras rendimiento UWP

Novedades en XAML

Con Windows 10 nos llegan un conjunto de novedades relacionados con la gestión de enlace a datos, nuevas etiquetas de marcado y otros detalles que afectan al rendimiento.

x:Bind

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…

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.

Veamos unas simples comparativas entre enlace a datos clásico y precompilado y su impacto en el rendimiento.

Uso de CPU utilizando enlace a datos clásico:

Uso de CPU en Bindings clásicos

Uso de CPU en Bindings clásicos

Uso de CPU utilizando bindings compilados:

Uso de CPU en binding compilado

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

Comparativa de consumo de memoria entre Bindings

Vista las visibles mejoras a nivel de rendimiento, ¿cómo se usan?.

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

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

<ListView
     ItemsSource="{Binding 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())
          });
     }
}

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.

Nuestro listado quedara:

<ListView
     ItemsSource="{Binding Houses}"
     ItemTemplate="{StaticResource HouseTemplate}" />
DataTemplate utilizando x:Bind

DataTemplate utilizando x:Bind

 

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.

Podéis descargar el ejemplo utilizando x:Bind a continuación:

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

Ver GitHub

x:Phase

Otra etiqueta nueva incluida en Windows 10, en este caso destinada a permitir realizar renderizado por fases. Con Windows 8.1 se introdujo en listados el evento ContainerContentChanging que nos permitía el renderizado progresivo de elementos. Requería código para actualizar la plantilla que dificultaba el uso de enlace a datos.

Ahora tenemos una nueva etiqueta llamada x:Phase que nos permite realizar con suma facilidad renderizado progresivo. Estableceremos valores numéricos.

NOTA: Por defecto el valor implícito es x:Phase=”0″.

<DataTemplate x:Key="XPhaseHouseTemplate" 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}" 
                 x:Phase="2"
                 MaxWidth="70" 
                 MaxHeight="70" />
             <TextBlock 
                 Text="{x:Bind Place}"  
                 Grid.Column="1" 
                 FontSize="18"/>
             <TextBlock 
                 Text="{x:Bind Price}"  
                 x:Phase="1"
                 Grid.Column="1"  
                 Grid.Row="1"
                 FontSize="12" />
     </Grid>
</DataTemplate>

El uso de x:Phase esta asociado al uso de bindings compilados.

Ejemplo de x:Phase:

También tenéis el código fuente disponible e GitHub:

Ver GitHub

x:DeferLoadStrategy

x:DeferLoadStrategy nos permite retrasar la creación de un elemento y sus elementos hijos lo que reduce los tiempos necesarios para la creación de la UI y por lo tanto de carga. Sin embargo, nada en la vida es gratis. A cambio, incrementamos levemente el consumo de memoria.

NOTA: Cada elemento que retrasamos en su inicialización con x:DeferloadStrategy añade 600 Bytes en el consumo de memoria.

Podemos deducir que a mayor cantidad de elementos que nos ahorremos del árbol visual, en menor tiempo se realizara la inicialización de la vista pero aumentando el consumo de memoria. Por lo tanto, el uso de la etiqueta es recomendado aunque requiere un análisis mínimo.

Creamos un ejemplo básico donde vamos a retrasar la creación de un Grid que contiene una imagen para crearlo bajo nuestro propio interés al pulsar el botón.

<Grid x:Name="DeferredPanel"  
      x:DeferLoadStrategy="Lazy">
      <Image
           Stretch="UniformToFill"
           Source="ms-appx:///Assets/NinjaCat.jpg" />
</Grid>

Utilizamos la etiqueta x:DeferLoadStrategy=”Lazy” en nuestro Grid. De esta forma indicamos que retrasamos la creación del panel y todo su contenido. Para utilizar la etiqueta debemos:

  • Definir un nombre con x:Name. Para iniciar posteriormente la incialización utilizaremos el nombre.
  • Podemos utilizarlo con cualquier elemento visual derivado de UIElement. No podemos utilizarlo con Page o UserControl.
  • No podremos utilizar con XAML XamlReader.Load.

Nos centramos ahora en el código que se ejecutará pulsando el botón:

page.FindName("DeferredPanel");

Utilizamos el método FindName pasándole el nombre del elemento.

Al pulsar el botón, el panel que retrasamos se crea. En este momento:

  • Se lanza el evento Loaded del panel.
  • Se evalúan los Bindings establecidos en el elemento.

Utilizando x:DeferLoadStrategy

Ejemplo de x:DeferLoadStrategy:

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Otras consideraciones

Virtualización

Tanto el control ListView como el control GridView soportan nativamente virtualización. Sin embargo, hay varios modos para perder la virtualización, debemos tener en cuenta:

  • Si envolvemos un control ListView o GridView sobre un ScrollViewer, su tamaño tiende a infinito, sin establecer límites perderíamos la virtualización.
  • Podemos organizar los elementos utilizando diferentes paneles. Paneles como por ejemplo WrapGrid no soporta virtualización.

Optimiza imágenes

Las imágenes de tamaños muy elevados, sobretodo si las vamos a mostrar en tamaños mucho mas reducidos, no compensan directamente debido a la enorme cantidad de memoria que consumen.

Se pueden utilizar las propiedades DecodePixelHeight y DecodePixelWidth de un BitmapImage para establecer el alto y ancho en el que se decodifica la imagen.

BitmapImage bi = new BitmapImage(new Uri(baseUri, path));
bi.DecodePixelHeight = 120;
bi.DecodePixelWidth = 180;

Ejemplo:

También tenéis el código fuente disponible e GitHub:

Ver GitHub

Optimiza textos

El renderizado de texto es en ocasiones hasta un 50% más rápido en Windows 10. Podemos aumentar el rendimiento usando:

  • CharacterSpacing
  • Typography
  • LineStackingStregy=BaselineToBaseline/MaxHeight
  • IsTextSelectionEnabled = true

Herramientas

Cada vez que nos llega a los desarrolladores un nuevo SDK, es un momento especial con una mezcla de altísima curiosidad y ganas de probar novedades. Entre las novedades principales siempre hay nuevas APIs, controles y otros elementos para poder realizar Apps que antes no eran posibles. Sin embargo, entre el conjunto de novedades siempre suelen venir nuevas herramientas que facilitan ciertas tareas: obtener más analíticas, mejores medidores de rendimiento, más opciones en emuladores, etc.

Visual Tree Inspector

Desde versiones anteriores de Visual Studio, una de las herramientas más demandadas son herramientas de depuración de UI XAML.

El árbol visual dinámico es la primera de dos piezas fundamentales para depurar UI XAML.

Visual Tree Inspector

Esta herramienta nos permite ver el árbol de controles de la App en ejecución indicando el número de elementos hijos de cada elemento ideal para entender la estructura visual de una vista compleja y entender problemas de rendimiento.

La segunda pieza relacionada con las herramientas de depuración de UI XAML es el explorador de propiedades dinámico.

Live Property Explorer

Esta herramienta nos permite ver todas las propiedades del elemento seleccionado, incluso aquellas sobreescritas. Podemos ver si las propiedades estan establecidas con valores directos, accediendo a recursos, etc. Además, y la parte más interesante, permite cambiar los valores de la App en ejecución directamente viendo los cambios de manera inmediata.

PerfTips

Generalmente y a pesar de contar con herramientas de diagnóstico, no se suelen utilizar hasta que surgen problemas, es decir, tarde. En estos casos, una vez detectados problemas de rendimiento, además de utilizar las herramientas de diagnóstico se suelen poner puntos de ruptura entre diferentes bloques para tener una idea de donde se pierde tiempo.

Estas prácticas no suelen ser muy buena idea. Por un lado se “caza” al problema cuando ya es un problema grande, y por otro lado, con puntos de ruptura o añadiendo líneas para obtener tiempos entre dos puntos del código, no suele ser muy exacto.

PerfTips llega para ayudar a a entender que ocurre en su aplicación a nivel de rendimiento mientras depura. En los puntos de ruptura aparecerán popups con información relacionada con el rendimiento.

PerfTips

PerfTips

PerfTips indica tiempos aproximados excluyendo los tiempos de pausa en un punto de ruptura así como la carga de símbolos y tiempo correspondiente al debugger.

Herramientas de diagnóstico

Las herramientas de diagnóstico son un conjunto de ventanas destinadas a ofrecer información relacionada con el rendimiento de la aplicación. Tenemos opciones para ver problemas de renderizado y parsing en la aplicación, monitorear consumo de Memoria y CPU o detectar problemas en el consumo de red entre otras opciones.

Consumo de CPU

Muestra el uso de CPU en todos los cores disponibles:

Consumo de CPU

Consumo de CPU

Consumo de memoria

Monitorea el consumo de memoria de la aplicación mientras estamos depurando.

Consumo de Memoria

Consumo de Memoria

Ahora disponible siempre tras cada sesión de depuración.

Renderizado y parsing

Identifica problemas de rendimiento relacionados con:

  • Parsing & Layout
  • Código de la App que provoca consume alto de CPU
Línea de tiempo

Línea de tiempo

Más información

[Windows 10] x:Bind, bindings compilados

Link - 05Introducció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

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

Bindings clásicos

Si utilizamos x:Bind:

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 en Bindings clásicos

Uso de CPU utilizando bindings compilados:

Uso de CPU en binding compilado

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

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

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