[Xamarin] Reporte de errores y analíticas con Mobile Center

Introducción

Una vez desarrollada una aplicación, recibir feedback sobre la misma es sumamente útil. Sería interesante saber información como cuanto tiempo estan los usuarios con nuestra aplicación, por que páginas navega, por donde nunca pasa el usuario, desde que dispositivos accede, errores, etc. Con esta información podemos sacar muchas conclusiones como detectar problemas de UX, añadir un mayor esfuerzo en zonas de la aplicación donde el usuario pasa la mayor parte del tiempo, esforzarnos por facilitar y promover el acceso a zonas apenas accedidas, corrección de bugs, etc.

¿Cómo podemos obtener toda esa información?

En este artículo, vamos a ver paso a paso como integrar una aplicación Xamarin.Forms con Visual Studio Mobile Center con el objetivo de descubrir y analizar errores en nuestras aplicaciones.

Mobile Center

Mobile Center es un centro de control de nuestras aplicaciones móviles. Mobile Center soporta tanto aplicaciones Xamarin como aplicaciones nativas con Java, Objective-C o Swift además de aplicaciones React Native.

Incluye:

  • Build: Integración y entrega continua.
  • Test: Pruebas automáticas en dispositivos reales.
  • Reporte de errores: Analíticas de errores en tiempo real.
  • Distribución: Despliegue a usuarios o grupos.
  • Analíticas: Métricas de uso.
  • Autenticación: Integración sencilla con métodos comunes.
  • EasyTables: Crear almacenamiento de forma muy rápida.

Crear la App

Accedemos al portal de Mobile Center.

Mobile Center

NOTA: Si no tienes cuenta de Mobile Center, puedes crear una desde este enlace.

Tras acceder al portal, debemos crear una nueva aplicación.

Nueva App

Se debe introducir el nombre de la aplicación, elegir Xamarin como plataforma y sistema operativo (iOS, Android o Windows). En este ejemplo, seleccionaremos Android.

Tras crear la aplicación, tenemos información disponible de que hacer para integrar Mobile Center en nuestra aplicación.

Pasos a realizar

Vamos a ello!

Preparar la App

Debemos gestionar los paquetes NuGet a nivel de solución (todos los proyectos que componen la solución Xamarin) para añadir las siguientes referencias:

  • Microsoft.Azure.Mobile.Analytics
  • Microsoft.Azure.Mobile.Crashes
Microsoft.Azure.Mobile.Analytics
Microsoft.Azure.Mobile.Crashes

Es hora de inicializar las analíticas. En la librería portable (PCL), en el archivo App.xaml.cs se deben añadir los siguientes using:

using Microsoft.Azure.Mobile;
using Microsoft.Azure.Mobile.Analytics;
using Microsoft.Azure.Mobile.Crashes;

A continuación, en el método OnStart que se lanzará al iniciar la App:

MobileCenter.Start("android={Insert App secret here}", typeof(Analytics), typeof(Crashes));

El código anterior, inicializa Mobile Center SDK en Android. Sin embargo, estamos en una aplicación Xamarin.Forms, ¿qué courre con el resto de plataformas?. Podemos inicializar más de una plataforma especificándola:

MobileCenter.Start("ios={Insert iOS App secret here};uwp={Insert UWP App secret here};android={Insert Android App secret here}", typeof(Analytics), typeof(Crashes));

Llegados a este punto, recibiremos analíticas en el portal como:

  • Actividad de usuarios
  • Sesiones por usuario diarias
  • Durración de cada sesión
  • Dispositivos más usados
  • Países
  • Actividad por versiones
  • Errores

Vamos a ver a continuación como registrar eventos personalizados o excepciones. Creamos una interfaz muy simple que nos permita hacer pruebas de registro de eventos y excepciones:

Nuestra App

Para registrar eventos personalizados utilizamos el método TrackEvent. Además de pasar una cadena con el nombre del evento, podemos utilizar un Dictionary para capturar diferentes parámetros.

var properties = new Dictionary<string, string>
{
     { "Parameter", "Data" }
};

Analytics.TrackEvent("EventBtn Clicked", properties);

¿Y qué debemos hacer para registrar excepciones?. En el arranque de la App inicializamos ya el registro de excepciones, no se requiere nada más. Si lanzamos con el segundo botón una excepción:

 throw new Exception("Exception information");

Quedará registrada.

Revisar analíticas

Accediendo al portal de Mobile Center, y entrando en nuestra App, tendremos acceso a analíticas desde los apartados Crashes y Analytics.

En Analytics tendremos información como:

  • Usuarios activos.
  • Sesiones diarias.
  • Duración decada sesión.
  • Dispositivos.
  • Versiones del sistema operativo.
  • Países.
  • Lenguajes.
Analíticas

Dentro del apartado de Analytics encontramos los eventos personalizados en Events:

Eventos

De nuevo tenemos información detallada como:

  • Listado de eventos.
  • Total de veces que se lanza el evento por usuario o por sesión.
  • Listado de parámetros.

Y llegamos a uno de los apartados más interesantes, Crashes:

Errores

Tenemos la traza del error detallado además de información extra que nos puede ayudar bastante como el dispositivo o versión del sistema operativo.

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

Ver GitHub

Más información

[Evento SVQXDG] De Java y Objective-C a .NET y de .NET a Java y Objective-C!

Introducción

Vuelta tras el verano!. Y como no podía ser de otra forma, volvemos con fuerza con un nuevo evento. Tras comenzar a conocer Xamarin algunas de las preguntas más habituales en muchos de los desarrolladores es:

  • ¿Puedo utilizar X librería Java en mi proyecto Xamarin?.
  • ¿Puedo utilizar X librería Objective-C en mi proyecto Xamarin?, ¿Y Swift?.
  • ¿Y al contrario?, ¿puedo utilizar mi librería .NET en un proyecto nativo?.

Para resolver a todas estas preguntas, desde SVQXDG, grupo de desarrolladores Xamarin de Sevilla, se organiza un evento donde veremos tanto las librerías de Bindings como Embedinnator-4000.

El evento

La agenda:

  • 19:00h – 20:00h: Librerías de Bindings: Tanto las comunidades de desarrolladores Android como de iOS cuentan con una gran variedad de fantásticas librerías nativas, desarrolladas en Java y Objective-C. ¿Cómo podemos utilizarlas desde nuestros desarrollos Xamarin?. En esta sesión veremos todo lo necesario para crear librerías de Bindings tanto en Android como en iOS. Con Juan María Lao.
  • 20:00h – 20:30h:  Embeddinator-4000: ¿Y si quiero utilizar mi fantástica librería .NET usada en Xamarin en desarrollos nativos?. Aquí es donde entra en juego Emveddinator-4000, una herramienta que permite generar librerías nativas tanto para iOS como para Android desde una librería .NET. Con Javier Suárez.

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

La fecha

Será el próximo Jueves , 14 de Septiembre a las 19:00h (GMT+1).

¿Te apuntas?

Más información

[Xamarin.Forms] MacOS Preview

Introducción

La evolución de Xamarin.Forms en los últimos años ha sido más que sorprendente. De igual forma que el interés y crecimiento de uso. Partía de una base sin duda atractiva, una capa de abstracción a nivel de UI que permite crear con el mismo código, C# o XAML, la interfaz de usuario en iOS, Android y Windows. Versión tras versión ha logrado llegar a permitir más posibilidades mejorando el soporte de cada plataforma, las posibilidades en XAML o el rendimiento (cabe destacar las mejoras en este punto). Hoy día se pueden hacer aplicaciones con resultados bastante sorprendentes.

Con la futura versión 3.0 en camino, entre las futuras mejoras encontramos la llegada de nuevos backends o lo que es lo mismo nuevas plataformas como WPF (Windows) o Linux (GTK#). Sin embargo, hoy día ya tenemos disponible como Preview una nueva plataforma, macOS.

macOS

En este artículo vamos a añadir un proyecto macOS y Xamarin.Forms al mismo. Veremos que con pocos sencillos pasos podemos lograr tener una aplicación para una nueva plataforma.

Configurar el proyecto macOS

Hasta tener plantillas de proyectos Xamarin.Forms con aplicación Cocoa, comenzaremos creando Mac > App > Cocoa App en Visual Studio para macOS.

Nueva App Cocoa

NOTA: Al igual que el desarrollo con Xamarin.Mac, el desarrollo para macOS requiere de un Mac.

Tras crear el proyecto, debemos añadir una librería portable (PCL) donde añadiremos el código común compartido con otros posibles backends Xamarin.Forms. Tras añadir el proyecto de la PCL, debemos añadir la referencia de la misma en el proyecto macOS.

A continuación, añadimos en ambos proyectos el paquete NuGet de Xamarin.Forms:

Paquetes NuGet de Xamarin.Forms

NOTA: La versión del paquete NuGet debe ser Xamarin.Forms 2.3.5.233-pre1 o superior.

La aplicación Cocoa viene con un Storryboard para configurar la interfaz de usuario y ciertos parámetros que no vamos a utilizar. Debemos editar el archivo Info.plist para eliminar la entrada NSMainStoryboardFile:

Modificar Info.plist

Llega el momento de realizar pequeños cambios de código para cargar la aplicación Xamarin.Forms. Comenzamos editando Main.cs:

NSApplication.SharedApplication.Delegate = new AppDelegate();

Inicializamos AppDelegate. En el delegado de la aplicación, vamos a inicializar Xamarin.Forms, crear una ventana y cargar la aplicación Xamarin.Forms.

public override void DidFinishLaunching(NSNotification notification)
{
     Forms.Init();
     LoadApplication(new App());
     base.DidFinishLaunching(notification);
}

Todo listo!

Probar la App

TipCalc macOS

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

Ver GitHub

Personalización por platafoma

La clase Device sin duda alguna es una de las claves a la hora de personalizar la interfaz de usuario por plataforma. Esta clase contiene diversas propiedades y métodos para personalizar la UI y la funcionalidad en base a la plataforma o el factor de forma.

Contamos con la propiedad RuntimePlatform que cuenta con un valor de una enumeración con cada una de las plataformas soportadas. Se ha incluido macOS y ahora tenemos también la posibilidad de crear código condicional para macOS en Xamarin.Forms:

if(Device.RuntimePlatform == Device.macOS)
{

}

Más información

[Xamarin.Forms] Aplicación móvil y Cosmos DB

Introducción

En una gran variedad de aplicaciones móviles se requiere almacenar datos. Podemos almacenar datos locales con opciones como SQLite o Realm; o en la nube con diferentes opciones. Entre las opciones disponibles en Azure, ha llegado con fuerza Cosmos DB.

En este artículo vamos a ver paso a paso como utilizar una base de datos de documentos Cosmos DB en una aplicación Xamarin.Forms. Realizaremos una aplicación que permita tomar, modificar y eliminar notas.

Cosmos DB

Una base de datos de documentos Azure Cosmos DB es una base de datos No SQL que proporciona acceso a información (JSON) de forma rápida, con alta disponibilidad, escalable y con replicación global.

Una base de datos de documentos en Azure Cosmos DB es un contenedor lógico para colecciones de documentos y usuarios. Puede contener cero o más colecciones. A su vez, cada colección de documentos puede tener un nivel de rendimiento diferente en base a la frecuencia de acceso. Cada colección de documentos cuenta con documentos JSON. A medida que se añaden documentos a una colección, Cosmos DB indexa automáticamente.

NOTA: Para pruebas y desarrollo, una base de datos de documentos se puede utilizar con un emulador. El emulador permite probar de forma local sin necesidad de suscripción Azure ni costes.

Crear Azure Cosmos DB

Accede al portal de Azure. Desde el menú lateral, Databases > Azure Cosmos DB:

Nueva Azure Cosmos DB

A continuación, debemos rellenar el formulario de creación:

Crear CosmosDB Azure

Donde:

  • API: Existen cuatro opciones diferentes: Gremlin (graph), MongoDB, SQL (DocumentDB), y tablas (key-value). En nuestro ejemplo usaremos DocumentDB.

Preparando el proyecto

Partiendo de una aplicación Xamarin.Forms, lo primero que necesitamos para realizar la integración con Azure Cosmos DB es añadir el paquete NuGet DocumentDB Client Library a la solución.

Microsoft.Azure.DocumentDB

La interfaz ded usuario

En nuestra aplicación contaremos con dos vistas, un listado de notas y una vista de detalles para crear, editar o eliminar una nota específica.

Comenzamos definiendo la vista principal. Tendremos un listado de notas:

<ListView 
     ItemsSource="{Binding Items}" 
     SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
     RowHeight="60">
     <ListView.ItemTemplate>
          <DataTemplate>
               <ViewCell>
                    <ViewCell.View>
                         <StackLayout 
                              Padding="12,0,12,0" 
                              HorizontalOptions="FillAndExpand">
                              <Label 
                                   Text="{Binding Notes}"
                                   FontSize="Medium"
                                   FontAttributes="Bold"/>
                              <Label 
                                   Text="{Binding Date}"
                                   FontSize="Small"/>
                         </StackLayout>
                     </ViewCell.View>
                </ViewCell>
           </DataTemplate>
      </ListView.ItemTemplate>
</ListView>

A parte de definir como se visualizará cada elemento de la lista definiendo el DataTemplate establecemos la fuente de información, propiedad ItemsSource enlazada a propiedad de la ViewModel que obtendrá los datos de la base de datos.

Además del listado, debemos añadir en nuestra interfaz una forma de poder insertar nuevas tareas. Para ello, una de las opciones más habituales e idóneas es utilizar una Toolbar.

 <ContentPage.ToolbarItems>
      <ToolbarItem
           Name="Add" 
           Command="{Binding AddCommand}">
           <ToolbarItem.Icon>
                <OnPlatform 
                     x:TypeArguments="FileImageSource"
                     Android="plus" 
                     iOS="plus.png" />
           </ToolbarItem.Icon>
      </ToolbarItem>
 </ContentPage.ToolbarItems>

La clase Device es muy importante en Xamarin.Forms ya que nos permite acceder a una serie de propiedades y métodos con el objetivo de personalizar la aplicación según dispositivo y plataforma. Además de permitirnos detectar el tipo de dispositivo, podemos detectar la plataforma gracias a la enumeración Device.OS o personalizar elementos de la interfaz gracias al método Device.OnPlatform entre otras opciones. En nuestro ejemplo, personalizamos el icono de añadir en base a la plataforma.

Nuestra interfaz:

Listado de notas

Enlazamos la View con la ViewModel estableciendo una instancia de la ViewModel a la propiedad BindingContext de la página.

BindingContext = App.Locator.NoteItemViewModel;

En la ViewModel contaremos con una propiedad pública para definir el listado de notas, además de la nota seleccionada (utilizada para la navegación):

private ObservableCollection<Note> _items;
private Note _selectedItem;

public ObservableCollection<Note> Items
{
     get { return _items; }
     set
     {
          _items = value;
          OnPropertyChanged();
     }
}

public Note SelectedItem
{
     get { return _selectedItem; }
     set
     {
          _selectedItem = value;
          _navigationService.NavigateTo<NoteItemViewModel>(_selectedItem);
     }
}

Añadimos elementos con un comando disponible en la ViewModel.

public ICommand AddCommand
{
     get { return _addCommand = _addCommand ?? new Command(AddCommandExecute); }
}

Al pulsar y lanzar el comando, navegaremos a la vista de detalles.

_navigationService.NavigateTo<NoteItemViewModel>(todoItem);

Si creamos un nuevo elemento pasaremos como parámetro una nueva entidad de tipo Note, en caso de seleccionar una existente, pasaremos el seleccionado disponible en la propiedad SelectedItem.

Definimos la interfaz de la vista de detalles:

<StackLayout 
     VerticalOptions="StartAndExpand" 
     Padding="20">
     <Label 
          Text="Notes" />
     <Entry 
          Text="{Binding Notes}"/>
     <Button 
          Text="Save"
          Command="{Binding SaveCommand}"/>
     <Button 
          Text="Delete"
          Command="{Binding DeleteCommand}"/>
     <Button 
          Text="Cancel"
          Command="{Binding CancelCommand}"/>
</StackLayout>

El resultado:

Detalles

Para enlazar la información de un elemento seleccionado, debemos capturar la información enviada en la navegación.

¿Cómo capturamos el elemento seleccionado en la navegación?. Utilizamos el método OnAppearing para capturar el parámetro NavigationContext.

public override void OnAppearing(object navigationContext)
{
     var note = navigationContext as Note;

     if (note != null)
     {
          Id = note.Id;
          Notes = note.Notes;
     }

     base.OnAppearing(navigationContext);
}

En cuanto a cada botón, cada uno de ellos estará enlazado a un comando:

private ICommand _saveCommand;
private ICommand _deleteCommand;
private ICommand _cancelCommand;

public ICommand SaveCommand
{
     get { return _saveCommand = _saveCommand ?? new Command(SaveCommandExecute); }
}

public ICommand DeleteCommand
{
     get { return _deleteCommand = _deleteCommand ?? new Command(DeleteCommandExecute); }
}

public ICommand CancelCommand
{
     get { return _cancelCommand = _cancelCommand ?? new Command(CancelCommandExecute); }
}

private void SaveCommandExecute()
{
 
}
 
private void DeleteCommandExecute()
{
 
}
 
private void CancelCommandExecute()
{
 
}

Consumiendo la base de datos Cosmos DB

Vamos a crear un servicio que contenga toda la lógica necesaria para trabajar con Cosmos DB desde nuestra aplicación Xamarin.Forms. DocumentClient encapsula la lógica necesaria relacionada con credenciales y conexiones con Azure Cosmos DB.

Comenzamos creando una instancia de DocumentClient:

var client = new DocumentClient(new Uri(EndpointUri), PrimaryKey);

En el constructor del cliente debemos pasar tanto la URL de conexión como la clave primaria. Ambos valores se pueden obtener desde el portal de Azure.

Clave Primaria

Con el cliente preparado, tenemos todo lo necesario para trabajar con Azure Cosmos DB.

Creando base de datos

De las primeras acciones a realizar sera la creación de la base de datos. Recuerda, una base de datos de documentos es un contenedor lógico para colecciones de documentos. Podemos crearla tanto desde el portal Azure como por código. Utilizaremos el método CreateDatabaseIfNotExistsAsync:

await _client.CreateDatabaseIfNotExistsAsync(new Database
{
     Id = databaseName
});

El método crea la base de datos con el identificador establecido en Id o bien, devuelve la base de datos si ya existe. Se devuelve Task<ResourceResponse<Database>> que contiene un código de estado, ideal para verificar si la base de datos se ha creado o si se ha obtenido una previamente existente.

Crear colección de documentos

Una colección de documentos es un contenedor de documentos JSON. Al igual que la base de datos, podemos crearlo en el portal o por código. Por código utilizamos CreateDocumentCollectionIfNotExistsAsync:

 await _client.CreateDocumentCollectionIfNotExistsAsync(
      UriFactory.CreateDatabaseUri(databaseName),
      new DocumentCollection
      {
           Id = collectionName
      });

El método requiere dos parámetros obligatorios (también cuenta con otros opcionales):

  • Nombre de base de datos: Especificado como Uri.
  • DocumentCollection: Representa una colección de documentos. Especificamos el nombre utilizando la propiedad Id.

El método crea la colección si no existe o devuelve la colección si ya existía previamente.

Obtener documentos de una colección

El contenido de una colección se puede obtener ejecutando una consulta. Podemos crear consultas utilizando el método CreateDocumentQuery. Por ejemplo:

var query = _client.CreateDocumentQuery<T>(_collectionLink) 
     .AsDocumentQuery();

while (query.HasMoreResults)
{
     items.AddRange(await query.ExecuteNextAsync<T>());
}

De esta forma podemos obtener todos los documentos de una colección específica. En el método CreateDocumentQuery se puede especificar vía Uri la colección de datos a la que conectar.

El método AsDocumentQuery convierte el resultado de la creación de la consulta en un objeto de tipo IDocumentQuery<T>. ExecuteNextAsync vamos obteniendo los resultados de la siguiente página (mientras exista más valores HasMoreResults).

Crear documento

Crear documentos cuyo contenido es en formato JSON se pueden insertar en una colección utilizando CreateDocumentAsync:

await _client.CreateDocumentAsync(_collectionLink, item);

Se especifica vía Uri la colección en la que se va a insertar el documento y el argumento de tipo objeto. El objeto es la información a insertar.

Eliminar documento

Se pueden eliminar documentos utilizando DeleteDocumentAsync:

await _client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(DatabaseName, CollectionName, id));

Se especifica vía Uri el documento de la colección a eliminar.

NOTA: También es posible eliminar toda una colección de documentos con DeleteDocumentCollectionAsync e incluso una base de datos completa con DeleteDatabaseAsync.

Llegados a este punto, ¿qué nos falta?. Sencillo, utilizar la lógica para interactuar con Azure Cosmos DB desde nuestras ViewModels. Crearemos la base de datos y la colección al navegar a la primera vista. En esta primera vista obtendremos todos los documentos creados (nuestras notas!). Desde la vista de detalles, en cada comando enlazado a la interfaz, utilizaremos la lógica para crear o eliminar notas.

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

Ver GitHubTe animo a descargar el ejemplo y realizar tus propias pruebas. Hasta aquí llegamos!. Recordad que podéis dejar cualquier comentario, sugerencia o duda en los comentarios.

Más información

[Xamarin.Forms] Extensiones de marcado personalizadas

Introducción

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.

En este artículo, vamos a conocer como crear nuestras propias extensiones de marcado.

RECUERDA: Ya vimos en un artículo anterior el concepto de extensión de marcado así como las extensiones de marcado disponibles.

 

Extensiones de marcado personalizadas

Las extensiones de marcado implementan la interfaz IMarkupExtension. Esta interfaz sólo cuenta con un método a implementar:

public object ProvideValue(IServiceProvider serviceProvider)

Cuando se realiza el análisis del código XAML y el compilador encuentra una extensión de marcado, se lanza este método antes de establecer el valor de la extensión.

Crear extensiones

Vamos a realizar una de las extensiones de marcado más sencillas posibles con el objetivo de centrarnos a fondo en los conceptos clave de la extensión. Nuestra extensión nos permitirá utilizar colores RGB estableciendo directamente los valores para rojo, verde y azul.

En el método ProvideValue, por defecto, no obtenemos valores con IServiceProvider. Por lo tanto, para obtener los valores necesarios en nuestra extensión (rojo, verde y azul) vamos a necesitar utilizar propiedades.

public class RGBColor : IMarkupExtension
{
     public int Red { get; set; }

     public int Green { get; set; }

     public int Blue { get; set; }

     public object ProvideValue(IServiceProvider serviceProvider)
     {
          return Color.FromRgb(Red, Green, Blue);
     }
}

Creamos una clase que hereda de IMarkupExtension, implementamos el método ProvideValue que se lanzará a la hora de evaluar la expresión. Gracias a propiedades capturamos los valores deseados. La lógica del método ProvideValue se encarga de crear el valor devuelto deseado, un color.

Todo listo!

Utilizar extensiones

Utilizar la extensión es sencillo. Necesitamos acceder a la misma, por lo tanto, debemos declarar el espacio de nombres en XAML en el lugar a utilizar:

xmlns:extensions="clr-namespace:CustomMarkupExtension.Extensions;assembly=CustomMarkupExtension"

Y utilizarlo:

<Label
     Text="Test"
     TextColor="{extensions:RGBColor Red=255, Green=0, Blue=255}"
     BackgroundColor="{extensions:RGBColor Red=200, Green=100, Blue = 0}" />

Fíjate que todo el código queda bastante simple y muy legible. Tenéis el código fuente del ejemplo utilizado disponible en GitHub:

Ver GitHubMediante la implementación de IMarkupExtension podemos crear nuestras propias extensiones de marcado. Las extensiones de marcado personalizadas nos dan una forma sencilla y potente de ejecutar código, realizar validaciones y conversiones de valores antes de establecer el valor de una propiedad.

Más información

[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