[Material] .Net Conf Sevilla 2018

El evento

El pasado 24 de Octubre, teníamos en CartujaDotNet un evento bastante completo donde revisamos las últimas novedades disponibles en Xamarin, C#, .NET Core, ML.Net, Azure, Azure DevOps, etc.

El material

A continuación puedes encontrar la presentación utilizada en el evento:

Una tarde de repaso de una gran variedad de tecnologías, con preguntas interesantes y algo de netwotking asi que, ¿cuándo repetimos?.

Más información

[Material SVQDC] Creando Apps para Hololens

9_t4cjcoEl evento

El pasado Sábado, 18 de Febrero tenía lugar la tercera edición de la Sevilla Developer Conference. Una iniciativa para fomentar tanto las distintas comunidades tecnológicas de Sevilla como tambien para darnos oportunidad de, durante un día, ver cómo resuelven sus retos en otras plataformas o lenguajes.

Como se puede ver en las fotos del evento, un enorme día de comunidad donde ver grandes sesiones, ver a antiguos amigos pero principalmente, para dar la oportunidad de conocer a muchos otros nuevos.

El material

He tenido la oportunidad de participar este año hablando de desarrollo para HoloLens.

Desarrollo para HoloLens
Desarrollo para HoloLens

Tras una breve introducción donde repasamos conceptos como realidad virtual, realidad aumentada o que es un holograma, entramos de lleno a conocer las HoloLens. Conbinando presentaciones, el dispositivo físico y el portal del dispositivo conocimos las características, sensores y posibilidades.

A continuación, entramos de lleno a hacer una App. Utilizando Wave Engine realizamos una aplicación sobre el sistema solar (en directo).

Terminamos con algunas aplicaciones desarrolladas ya publicadas en la Store, consejos y preguntas. Durante la sesión fue muy divertido ver las expresiones y el feedback de aquellos que pudieron probarlas!.

La presentación:

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

Ver GitHub

Más información

[Material] Containers y Xamarin everywhere!

El evento

El pasado Martes, 07 de Febrero en la E.T.S. Ingeniería Informática tenía lugar un evento de las comunidades, CartujaDotNet, grupo de usuarios .NET de Sevilla y SVQXDG, grupo de desarrolladores Xamarin de Sevilla aprovechando una visita a la Universidad de los chicos de Microsoft DX. El evento que tenía como título «Containers y Xamarin everywhere!» proponía una divertida tarde donde hacer una introducción a Containers y Docker de la mano de Diego Martínez y posteriormente una sesión sobre testing de Apps Xamarin de la mano de un servidor.

Containers
Containers

El material

He tenido la oportunidad de participar en una de las sesiones tratando la creación y mantenimiento de pruebas automáticas de aplicaciones Xamarin. Concretamente vimos:

  • Introducción al concepto de pruebas unitarias.
  • Como crear pruebas unitarias a un proyecto Xamarin.
  • Conceptos básicos necesarios de Xamarin.UITest.
  • Crear proyecto de tipo Xamarin.UITest.
  • Introducción a Xamarin Test Cloud.
  • Lanzar pruebas de tipo UITest en Test Cloud.
  • Introducción a Visual Studio Mobile Center.

La presentación:

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

Ver GitHub

Quisiera terminar agradeciendo a Diego Martínez por su colaboración y sesión en el evento, a la E.T.S. Ingeniería Informática y al club .NET de la universidad por su ayuda y facilidades a la hora de disponer de sala y por supuesto a todos los asistentes.

Nos vemos en la próxima!

Más información

[VideoBlog] Novedades en la Preview 2 de Tizen.NET

tizen_logoIntroducción

El pasado Microsoft Connect 2016, en San Francisco, Samsung en colaboración con Microsoft anunciaba la primera versión en modo Preview de las herramientas Tizen para Visual Studio.

Las herramientas Tizen para Visual Studio facilitan emuladores, extensiones para Visual Studio con la posibilidad de tener IntelliSense y capacidades de depuración, se utiliza Xamarin.Forms para definir la interfaz de usuario, como ya hacíamos con iOS Y Android. De esta forma, Xamarin.Forms añade una nueva plataforma a la que dar soporte ampliando las posibilidades del mercado.

Tizen.NET Preview 2

Recientemente, se ha liberado la Preview 2 de Tizen.NET. Una actualización importante que añade una enorme cantidad de novedades con respecto a la priemera versión lanzado el pasado Noviembre.

Tizen.NET Preview 2
Tizen.NET Preview 2

Entre el conjunto de novedades más detacadas encontramos:

  • Soporte a TV: En la primera release se incluía el soporte a .NET Standard, Xamarin.Forms y APIs Tizen de móviles. Ahora se incluyen todo lo necesario para desarrollar aplicaciones para televisores.
  • Asistente de proyecto: Dado que ahora contamos con diferentes tipos de posibles proyectos (móviles y TV), al crear un nuevo proyecto para Tizen tendremos un sencillo asistente que nos permitirá configurar las opciones básicas.
  • Nuevas herramientas: Gestión de emuladores, administrador de certificados o editor de manifiesto. Se han añadido varias herramientas nuevas para permitir desarrollar aplicaciones de forma más sencilla.
  • Nuevas APIs: Incremento alto del conjunto de APIs Tizen soportadas con respecto a la Preview anterior. Entre las novedades encontramos uso de NFC, gestión y administración de multimedia, geofence, etc.

Ante tal conjunto de novedades, ¿algo mejor que verlas todas en video?

Más información

Un vistazo al Xamarin Partner Summit 2017 de Londres

xamarinpartnerIntroducción

Hace poco he tenido la oportunidad de asistir al Xamarin Partner Summit de Londres representando a Plain Concepts.

Xamarin Partner Summit

Estamos ante un evento de un día celebrado en múltiples lugares (Londres, Amsterdam y Silicon Valley) donde se reunen y agrupan los partners de Xamarin con el objetivo de facilitar la comunicación y el networking además de tener algunas sesiones técnicas e información relacionada con el programa.

Xamarin Partner Summit
Xamarin Partner Summit

Keynote

La Keynote del evento con Greg Urquhart se enfocó a analizar el mercado móvil actual. Con una previsión de más de 6 billones (OJO con b) de usuarios con teléfonos inteligentes en el mercado para el año 2020, el equivalente al 70% de la población mundial estamos ante un mercado lleno de posibilidades. Apps have won. Tras un repaso a la estrategia «Mobile first, Cloud first» llevada a cabo por Microsoft en los últimos años, se profundizó en el camino llevado a cabo hasta conseguir ser el mayor contribuidor de código Open Source de GitHub, la adquisión de Xamarin y la expansión de Azure.

Las sesiones técnicas avanzadas

Tras la Keynote podíamos elegir entre diferentes Tracks:

  • Negocio
  • Nivel medio técnico
  • Nivel avanzado técnico

A ser desarrollador y contar con experiencia en el desarrollo me aventuré a lanzarme al track de desarrollo técnico avanzado. Divida en dos partes, la primera parte fue un conjunto bastante interesante y diverso de puntos y consejos donde tuvimos a Colby Williams. Se trataron diversos puntos entre los que destacaría:

  • Ventajas e incoventienes de PCL vs Shared. Independientemente de favoritos, pros y contras, la clave es su uso en conjugación con buenas prácticas para poder compartir la mayor cantidad de código posible.
  • Reutilización de código. Uno de los puntos claves que se trataron fue la reutilización de código. Pero no reutilizar código entre diferentes plataformas en una misma aplicación. El factor que se trataba era reutilizar código entre diferentes aplicaciones. El beneficio de crear paquetes NuGet, versionar y reutilizar las versiones adecuadas en diferentes aplicaciones.
  • Olvidad el porcentaje. Suena extraño cuando durante mucho tiempo se ha utilizado el porcentaje de código compartido como signo de salud, buen hacer y ventajas del proyecto Xamarin. Colby indicó que de nada sirve compartir un poco más de código si esto afecta al rendimiento o complica el mantenimiento de la aplicación. Al final, el usuario final utiliza una aplicación este realizada como este realizada, la clave es poner el enfoque en la reutilización, rendimiento y mantenimiento. El porcentaje es una consecuencia, no el factor principal.
  • No reinventar la rueda. En muchas ocasiones se ven implementaciones similares o dudas sobre aspectos que se encuentran muy bien cubiertos. Conocer los plugins que aporta la propia Xamarin y la comunidad.

Es un pequeño extracto de una sesión donde también tuvimos tiempo para hablar de otros aspectos como la distribución y reutilización de librerías o Visual Studio Mobile Center.

La segunda parte de esta sesión se basó en una mesa redonda. Fue una «lluvia»  brutalmente interesante de puntos como, aspectos que necesita integrar Visual Studio Mobile Center para poder cubrir las posibilidades de VSTS Build + Xamarin Test Cloud + HockeyApp, rendimiento en aplicaciones con diferentes consejos y prácticas (Ojo a la suscripción a eventos sin posteriormente eliminar el event handler delegate), patrón MVVM y frameworks en combinación con aspectos relacionados con el rendimiento (bindings principalmente), etc.

En otras sesiones correspondiente a otros Tracks se vieron otros puntos o algunos similares desde otro punto de vista (no pude asistir, lo capté hablando con otros compañeros). Cabe destacar la mención especial a lo que se considera como otra gran oportunidad que llegará proximamente, la realidad aumentada / realidad virtual y realidad mixta.

Cierre y networking

Llegábamos a la parte final del evento con una Keynote de cierre. En este punto vimos los próximos pasos del programa de partners de Xamarin que pasará a ser MPN (Microsoft Partner Network) desde el próximo año fiscal (Julio). Repasamos ideas claves, próximos eventos y fin. Fin de las sesiones y llegada de un momento divertido de networking. Pude conocer a otros compañeros Microsoft y Xamarin MVP de otros países, otros desarrolladores Xamarin y las conversaciones fueron muy variadas y nutritivas. Vi alguna aplicación interesante, igualmente mostré algunas de las desarrolladas, etc.

Y hasta aquí!.

[Xamarin] Utilizando Realm

RealmSquareIntroducción

El trabajo con datos en dispositivos móviles se ha convertido ya en algo común y habitual en el desarrollo de aplicaciones. Existe una gran variedad de tipos de datos y formas de almacenamiento:

  • Archivos de texto. Texto plano o html cacheado en el espacio de almacenamiento aislado de la aplicación.
  • Imágenes. En el espacio de almacenamiento aislado de la aplicación o almacenadas en directorios conocidos del sistema.
  • Archivos serializados. Archivos XML o Json con objetos serializados.
  • Bases de datos. Cuando se requieren datos estructurados, obtener información más compleja con consultas avanzadas entre otro tipo de necesidades, la posibilidad de las bases de datos es la elección idónea.

Realm

SQLite, engine de base datos open source es la opción más extendida y usada en el desarrollo de aplicaciones móviles. Recientemente, se ha añadido soporte a Xamarin de Realm.

Realm es una base de datos gratuita pensada para aplicaciones móviles, tabletas o wearables siendo una alternativa interesante a SQLite. Con el gran objetivo en mente de conseguir un alto rendimiento manteniendo una alta facilidad de uso, a lo largo de este artículo, crearemos una aplicación real con Xamarin.Forms y Realm donde poder crear, editar y gestionar un listado To Do.

Arrancando el proyecto

Comenzamos creando una aplicación Xamarin.Forms utilizando una librería portable (PCL):

Nueva aplicación Xamarin.Forms
Nueva aplicación Xamarin.Forms

Tras crear la aplicación, añadimos las carpetas básicas para aplicar el patrón MVVM además del paquete NuGet de Unity para la gestión del contenedor de dependencias.

Estructura del proyecto
Estructura del proyecto

Añadiendo Realm

Con el proyecto y estructura base creada, vamos a añadir Realm al proyecto. Realm esta disponible en NuGet. Vamos a añadir en cada proyecto de la solución la última versión disponible del paquete utilizando NuGet.

Hacemos clic derecho sobre la solución, Administrar paquetes NuGet para la solución…

NuGet PackagesBuscamos por la pabara «Realm» e instalamos el paquete en todos los proyectos.

Realm
Realm

La definición de modelos

En nuestra aplicación, trabajaremos con elementos del listado ToDo, una única entidad sencilla.

public class TodoItem : RealmObject
{
     public int Id { get; set; }
     public string Name { get; set; }
     public string Notes { get; set; }
     public bool Done { get; set; }
}

Los modelos en Realm son clases tradicionales con propiedades. La clase debe heredar de RealmObject para definir los modelos de datos de Realm. El trabajo con el modelo es como con cualquier modelo tradicional. Se puede añadir variables y lógica personalizada, la única restricción es que solo podremos almacenar la información de las propiedades con get y set.

A pesar de que en nuestro ejemplo no tengamos la necesidad de establecer relaciones entre múltiples modelos, podemos realizarlas de forma sumamente sencilla mediante el uso de RealmList.

En cuanto a los tipos soportados, podemos usar todos los tipos básicos (char string, int, float, double, long) además de DateTimeOffset.

La interfaz de usuario

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

Vistas
Vistas

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

<ListView 
    ItemsSource="{Binding Items}"
    SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <ViewCell.View>
            <StackLayout 
              Padding="20,0,20,0"                       
              Orientation="Horizontal"       
              HorizontalOptions="FillAndExpand">
              <Label Text="{Binding Name}"
                     VerticalTextAlignment="Center"
                     HorizontalOptions="StartAndExpand" />
              <Image Source="check.png"
                     HorizontalOptions="End"
                     IsVisible="{Binding Done}"/>
            </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.

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

BindingContext = App.Locator.TodoItemViewModel;

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

private ObservableCollection<TodoItem> _items;
private TodoItem _selectedItem;
 
public ObservableCollection<TodoItem> Items
{
     get { return _items; }
     set
     {
          _items = value;
          RaisePropertyChanged();
     }
}
 
public TodoItem SelectedItem
{
    get { return _selectedItem; }
    set
    {
          _selectedItem = value;
    }
}

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"
                    WinPhone="Assets/add.png" />
      </ToolbarItem.Icon>
    </ToolbarItem>
</ContentPage.ToolbarItems>

Añadimos un ToolbarItem que permitirá añadir elementos.

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:

Vista principal
Vista principal

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

private ICommand _addCommand;
 
public ICommand AddCommand
{
     get { return _addCommand = _addCommand ?? new DelegateCommand(AddCommandExecute); }
}
 
private void AddCommandExecute()
{
 
}  

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

_navigationService.NavigateTo<TodoItemViewModel>(_selectedItem);

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

Definimos la interfaz de la vista de detalles:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TodoRealm.Views.TodoItemView"
             Title="{Binding Name}">
  <StackLayout 
    VerticalOptions="StartAndExpand"
    Padding="20">
    <Label 
      Text="Name" />
    <Entry 
      Text="{Binding Name}"/>
    <Label 
      Text="Notes" />
    <Entry 
      Text="{Binding Notes}"/>
    <Label 
      Text="Done" />
    <Switch 
      x:Name="DoneEntry"
      IsToggled="{Binding Done, Mode=TwoWay}"/>
    <Button 
      Text="Save"
      Command="{Binding SaveCommand}"/>
    <Button 
      Text="Delete"
      Command="{Binding DeleteCommand}"/>
    <Button 
      Text="Cancel"
      Command="{Binding CancelCommand}"/>
  </StackLayout>
</ContentPage>

Añadimos cajas de texto para poder editar toda la información de una tarea además de botones para poder guardar, borrar o cancelar y navegar atrás.

El resultado:

Detalle
Detalle

Para enlazar la información de un elemento seleccionado, debemos capturar la información enviada en la navegación. Creamos una propiedad pública para enlazar con la UI de la tarea:

private TodoItem _item;
 
public TodoItem Item
{
     get { return _item; }
     set { _item = value; }
}

¿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 todoItem = navigationContext as TodoItem;
 
     if (todoItem != null)
     {
          Item = todoItem;
     }
 
     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 DelegateCommand(SaveCommandExecute); }
}
 
public ICommand DeleteCommand
{
     get { return _deleteCommand = _deleteCommand ?? new DelegateCommand(DeleteCommandExecute); }
}
 
public ICommand CancelCommand
{
     get { return _cancelCommand = _cancelCommand ?? new DelegateCommand(CancelCommandExecute); }
}
 
private void SaveCommandExecute()
{
 
}
 
private void DeleteCommandExecute()
{
 
}
 
private void CancelCommandExecute()
{
 
}

Trabajando con Realm

Llegados a este punto, tenemos la estructura, vistas y lógica básica necesaria de toda la aplicación. Sin embargo, aún nos falta la parte clave de la aplicación, el trabajo con Realm.

Trabajaremos con la base de datos en Realm definiendo una instancia de tipo Realm utilizando el método Realm.GetInstance(), no es más que un objeto que encapsula la base de datos.

private Realms.Realm _realm;
_realm = Realms.Realm.GetInstance();

Para hacer consultas tenemos disponible el método Realm.All<>(). Con Realm podemos utilizar LINQ para realizar consultas de la información (filtros, ordenaciones, etc.).

public IList<TodoItem> GetAll()
{
     var result = _realm.All<TodoItem>().ToList();
 
     return result;
}

De esta forma obtenemos el listado de tareas almacenadas en la base de datos para enlazar con el listado de la vista principal.

RealmObject permite auto refrescar la información, es decir, un cambio en una propiedad de un objeto tiene un efecto inmediato en cualquier otra instancia haciendo referencia.

Cualquier cambio sobre un objeto (insertar, actualizar o eliminar) debe realizarse usando una transacción

Tenemos dos formas sencillas de crear transacciones, utilizando Realm.BeginWrite() y con Realm.Write(). Ambos devueven una Transaction e implementan el patrón Dispose.

public void Insert(TodoItem item)
{
     var items = _realm.All<TodoItem>().ToList();
     var existTodoItem = items.FirstOrDefault(i => i.Id == item.Id);
             
     if (existTodoItem == null)
     {
          _realm.Write(() =>
          {
               var todoItem = _realm.CreateObject<TodoItem>();
               todoItem.Id = items.Count + 1;
               todoItem.Name = item.Name;
               todoItem.Notes = item.Notes;
               todoItem.Done = item.Done;
          });
     }
     else
     {
          using (var trans = _realm.BeginWrite())
          {
               existTodoItem.Name = item.Name;
               existTodoItem.Notes = item.Notes;
               existTodoItem.Done = item.Done;
 
               trans.Commit();
          }
     }
}

NOTA: En el caso de utilizar BeginWrite es importante tener en cuenta que debemos hacer Commit de la transacción.

Eliminar es una acción muy sencilla, basta con seleccionar el elemento a eliminar, crear una transacción y eliminar utilizando el métoto Remove.

public void Remove(TodoItem item)
{
     var items = _realm.All<TodoItem>().ToList();
     var existTodoItem = items.FirstOrDefault(i => i.Id == item.Id);
 
     if (existTodoItem != null)
     {
          using (var trans = _realm.BeginWrite())
          {
               _realm.Remove(existTodoItem);
               trans.Commit();
          }
     }
}

NOTA: También se pueden eliminar todos los objetos almacenados e Realm.

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

Ver GitHub

A tener en cuenta

La facilidad de uso, potencia y opciones de Realm hace que ya sea una opción interesante en el uso de datos estructurados en nuestra aplicación. Sin embargo, su nivel ed maduración es inferior al de SQLite. Contamos con algunas limitaciones como:

  • No se puede borrar en cascada.
  • Consultas asíncronas.
  • Notificaciones en colecciones.
  • Migraciones.
  • Etc.

One more thing

Exactamente el mismo ejemplo, con las mismas entidades, vistas y lógica realizo con SQLite lo tenéis también disponible en GitHub:

Ver GitHub

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

Más información

VideoBlog, nuevo formato en el Blog!

YouTubeNovedad en el blog

Son ya varios años de vida de este modesto Blog. En todo este tiempo de forma periódica se han ido publicando artículos sobre desarrollo, quedadas, eventos y otras entradas con el objetivo de ayudar en la medida de lo posible a la comunidad. Sin duda, un formato que encaja, me divierte y sin duda seguirá. Sin embargo, en ocasiones, hay entradas que por tamaño se deben dividir en varias partes, desarrolladores interesantes que me permiten probar una App o una herramienta de desarrollo o ocasiones en las que lo que se intenta trasmitir es una experiencia más que un conocimiento puro. Para todas estas situaciones, se añadirán junto a las habituales, un nuevo formato al blog.

Nuevo formato

Para los casos anteriores, experiencias, desarrollos paso a paso que requieren mayor dedicación o momentos con otros desarrolladores para analizar nuevas herramientas o Apps nacen las «video entradas». Serán entradas que combinarán una parte escrita pero apoyadas con la potencia y versatilidad de un video. En ocasiones serán videos grabados previamente y lanzados en conjunto con la entrada mientras que, en ocasiones de mayor interés, se realizarán videos con streaming en directo permitiendo la participación de todos por supuesto guardando la grabación para su visionado a posteriori.

Más información

[Tips and Tricks] Windows 10. Adaptar recursos por DPI de pantalla

Scale to Fit-02Introducción

Con Windows 10 los desarrolladores recibimos la culminación de un gran viaje en la convergencia entre las diferentes plataformas Windows. Ahora podemos desarrollar aplicaciones para gran diversidad de familias de dispositivos como móviles, PCs, tabletas, IoT y otros que están por llegar, compartiendo la mayor cantidad de código, con un mismo proyecto y paquete. Además, contamos con grandes nuevas características como Continuum en teléfonos que permite convertirlo en un PC utilizando Microsoft Display Dock o Miracast.

Sin embargo, hay un detalle claro y obvio. Si contamos con un mismo paquete para todos esos dispositivos diferentes…¿cómo adaptamos la experiencia para ofrecer la mejor opción adaptada posible?

Debemos adaptar la interfaz de usuario en cada familia de plataforma para lograr ofrecer la mejor experiencia posible adaptada a la perfección. Para ello, utilizamos:

  • AdaptiveTriggers
  • Nuevos controles como RelativePanel y/o SplitView
  • Detección de modos de interacción
  • Etc

Sin embargo, hay elementos vitales en la mayoría de aplicaciones, que no acaban recibiendo la atención que se merecen. Estoy hablando de las imagenes. La aplicación puede usar una imagen que se visualiza perfectamente en un teléfono pero…¿y si se usa la aplicación con Continuum en una pantalla con una resolución diferente (más elevada)?.

En este artículo, vamos a aprender como organizar y utilizar los recursos de la aplicación para que se utilicen y adapten por DPI.

DisplayInformation

La clase DisplayInformation cuenta con propiedades y eventos que nos permiten verificar y monitorear información relacionada con la pantalla física. Para monitorear detalles como cambios en DPI o la rotación podemos usar la clase DisplayInformation.

En nuestra interfaz vamos a mostrar la siguiente información:

<StackPanel
     Orientation="Horizontal">
     <TextBlock Text="Logical DPI:" />
     <TextBlock Text="{Binding LogicalDpi}" />
     <TextBlock Text="DPI" />
</StackPanel>

<StackPanel
     Orientation="Horizontal">
     <TextBlock Text="Scaling:" />
     <TextBlock Text="{Binding Scale}" />
     <TextBlock Text="%" />
</StackPanel>

De modo que, en la viewmodel bindeada definiremos dos propiedades:

private string _logicalDpi;
public string LogicalDpi
{
     get { return _logicalDpi; }
     set
     {
          _logicalDpi = value;
          RaisePropertyChanged();
     }
}

private string _scale;
public string Scale
{
     get { return _scale; }
     set
     {
          _scale = value;
          RaisePropertyChanged();
     }
}

Una para cada valor que deseamos mostrar en pantalla. Utilizaremos el método DpiChanged lanzado cada vez que la propiedad LogicalDpi se modifica, cuando cambian los píxeles por pulgada (PPI) de la pantalla.

private DisplayInformation _displayInformation;

_displayInformation = DisplayInformation.GetForCurrentView();

Tras obtener la información física actual de la pantalla utilizando el método GetForCurrentView nos suscribimos al evento DpiChanged:

_displayInformation.DpiChanged += _displayInformation_DpiChanged;

Cada vez que el evento se lanza, actualizamos la información mostrada en pantalla:

private void _displayInformation_DpiChanged(DisplayInformation sender, object args)
{
     DisplayInformation displayInformation = sender as DisplayInformation;

     UpdateDpi(displayInformation);
}

private void UpdateDpi(DisplayInformation displayInformation)
{
     if (displayInformation != null)
     {
          LogicalDpi = displayInformation.LogicalDpi.ToString();
          Scale = (displayInformation.RawPixelsPerViewPixel * 100.0).ToString();
     }
}

Mostramos los píxeles por pulgada lógica de la pantalla actual utilizando la propiedad LogicalDpi, mientras que para la escala utilizamos la propiedad RawPixelsPerViewPixel que indica el número de píxeles físicos (RAW) por cada  pixel mostrado (Layout). Para obtener la escala bastará con multiplicar el valor por cien.

NOTA: En este ejemplo utilizamos la clase DisplayInformation para mostrar información contextual relacionada con el escalado de imágenes utilizado. Sin embargo, utilizando propiedades como DiagonalSizeInInches podemos saber facilmente el tamaño en pulgadas de la pantalla y así adaptar la interfaz en consecuencia. Sumamente útil y sencillo combinado con el uso de AdaptiveTriggers personalizados.

Recursos por DPI

Para optimizar nuestra interfaz en cada posible dispositivo o condición, podemos facilitar diferentes assets para diferentes resoluciones y escalas. Cada dispositivo cuenta con una escala específica resultante de la densidad de píxeles física y la distancia de visión teórica.

La escala es utilizada por el sistema, que realiza una gestión de recursos para determinar que recurso es el más adecuado entre las opciones facilitadas por los desarrolladores en sus aplicaciones.

NOTA: Los teléfonos suelen tener una escala de entre 200 y 400 mientras que dispositivos conectados como monitores y TVs tiene valores de 100 y 150 respectivamente.

Ejemplos de escala
Ejemplos de escala

Añadiendo recursos por escala

Para soportar el uso de diferentes recursos dependientes de la escala, bastará con añadirlos de la forma adecuada. Contamos con dos formas diferentes para ello.

Por un lado, podemos sencillamente añadir el mismo recurso con diferentes tamaños utilizando la notación .scale-xxx dentro de la carpeta Assets:

Recursos por DPI
Recursos por DPI

Por otro lado, podemos añadir diferentes carpetas con el nombre de la escala, es decir, scale-xxx incluyendo como contenido los recursos.

Utilizando recursos por escala

En nuestra carpeta de Assets contamos con una imágen única llamada Picture-NoScale:

<Image
     Source="ms-appx:///Assets/Picture-NoScale.png"
     Height="100"
     Width="100"
     HorizontalAlignment="Left"/>

Con el código anterior, usaremos la misma (y única) imágen existente bajo cualquier condición. Si la escala es alta y requiere de recursos con mayor resolución, el resultado será una visualización borrosa de la misma. Proporcionamos una experiencia no idónea.

Contamos con múltiples opciones por escala del  recurso llamado Picture, bastará con utilizarlo ignorando scale-xxx de la ruta:

<Image
    Source="ms-appx:///Assets/Picture.png"
    Height="100"
    Width="100"
    HorizontalAlignment="Left"/>

Utilizando los recursos vistos previamente, donde en un caso usamos una imágen única bajo cualquier condición y en otro una adapada a diferentes escalas, el resultado es el siguiente:

Carga de Assets por DPI
Carga de Assets por DPI

Tenemos 192DPI con una escala de 200%. Podemos ver a simple vista que mientras que la primera imágen se visualiza pixelada, la segunda se aprecia con una calidad alta.

Sencillo, ¿cierto?

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

Ver GitHub

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

Más información