[Windows 10 Anniversary Update] Novedades en 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 10 nos llegaba la posibilidad de crear bindings compilados en lugar de los bindings clásicos. Ya vimos previamente su uso, pros y contras en este artículo. Ahora, con la llegada del Anniversary Update nos llegan una gran cantidad de novedades relacionadas con el desarrollo que iremos desgranando poco a poco, entre las que se incluyen novedades en x:Bind.

¿En qué consisten?, ¿qué aportan?

En este artículo vamos a centrarnos en el conjunto de novedades incluidas en el sistema de enlace a datos compilados en la actualización Anniversary Update.

Enlace a métodos

Previamente, desde la versión 1607 de Windows, {x:Bind} soportaba un evento como parte del enlace a datos.

Veamos un ejemplo. Vamos a crear una interfaz mostrando un listado de casas. La colección de casas:

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

Que cargaremos con sencillos datos locales:

private void LoadHouses()
{
     _houses = new ObservableCollection<House>();
 
     _houses.Add(new House
     {
          Name = "Home 1"
     });
     _houses.Add(new House
     {
          Name = "Home 2"
     });
}

La interfaz de usuario, un control ListView enlazado con un enlace compilado a la colección:

<ListView
     ItemsSource="{x:Bind Houses}"
     ItemTemplate="{StaticResource HouseDataTemplate}" />

En cuanto a la plantilla que define cada elemento del listado:

<DataTemplate
     x:Key="HouseDataTemplate"
     x:DataType="model:House">
     <Grid>
          <Grid.ColumnDefinitions>
               <ColumnDefinition Width="Auto" />
               <ColumnDefinition Width="*" />
          </Grid.ColumnDefinitions>
          <TextBlock 
               Text="{x:Bind Name}"
               x:Phase="1"
               VerticalAlignment="Center" />
          <Button 
               Grid.Column="1"
               Content="Click me!"
               Click="{x:Bind Click}" />
     </Grid>
</DataTemplate

Se utiliza x:Bind para enlazar las propiedades, estableciendo el tipo de los datos a enlazar mediante la propiedad x:DataType.

NOTA: Recuerda que x:Bind está fuertemente tipado.

¿Ves algo «raro»?. El botón incluido dentro de la plantilla enlaza el evento Click a «algo» llamado Click.

Hasta ahora no habíamos visto la definición de la entidad House:

public class House
{ 
     public string Name { get; set; }
 
     public void Click(object sender, RoutedEventArgs e)
     {
          Debug.WriteLine(string.Format("Clicked {0}!", Name));
     }
}

Ahora probablemente has deducido que ocurre. Se pueden realizar enlace a datos directamente a eventos. Se necesita en el evento:

  • No tener parámetros. Ejemplo void Click().
  • Coincidir los parámetros con los parámetros del evento. Ejemplo: void Click(object sender, RoutedEventArgs e).
  • O coincidir con los tipos base de los parámetros del evento. Ejemplo: void Click(object sender, object e).

Al pulsar el botón de cada plantilla, se desencadena el evento Click que tenemos disponible en la entidad.

El resultado:

Event Bindings
Event Bindings

Ahora, tras la actualización Anniversary Update, se da un paso más permitiendo enlazar con cualquier método sin necesidad de un converter. Las únicas condiciones que debe complir el método es que sea público y en caso de tener parámetros, facilitarlos. Una forma de:

  • Obtener conversión de valores avanzada.
  • Tener un enlace a datos que dependa de más de un parámetro.

Veamos un ejemplo. En algunos formularios donde hay contraseñas involucradas se ofrece feedback visual de la seguridad de la contraseña introducida. Vamos a mostrar una barra de color rojo, naranja o rojo dependiendo de la seguridad.

Tendremos una propiedad donde se enlazará la contraseña:

public string UserPassword { get; set; }

En la UI:

<ProgressBar 
     Background="Transparent"
     Foreground="{x:Bind PasswordStrengthBrush(Password.Password), Mode=OneWay}"
     Value="{x:Bind PasswordStrengthValue(Password.Password), Mode=OneWay}"
     Height="3"/>
<PasswordBox 
     x:Name="Password"
     PlaceholderText="Insert Password"      
     Password="{x:Bind UserPassword, Mode=TwoWay}" />

Un control PasswordBox encargado de capturar la contraseña (utilizando x:Bind), y en la parte superior un ProgressBar que mostrará más o menos recorrido y distintos colores dependiendo de la seguridad de la contraseña introducida.

Fíjate en el enlace a datos de Foreground y Value del PasswordBox. ¿Qué está pasando?.

Para modificar el color se enlaza a un método público llamado PasswordStrengthBrush que recibe como parámetro la contraseña (directamente accediendo a la propiedad Password del PasswordBox!).

En cuanto al valor, de nuevo, se hace un enlace a un método público llamado PasswordStrengthValue recibiendo como parámetro de nuevo la contraseña.

El primero de los métodos:

public SolidColorBrush PasswordStrengthBrush(string password)
{
     if (string.IsNullOrEmpty(password))
          return new SolidColorBrush(Colors.Transparent);
 
     int length = password.Length;
 
     if (length >= 3 && length < 6)
          return new SolidColorBrush(Colors.Orange);
     if (length >= 6)
          return new SolidColorBrush(Colors.Green);
 
     return new SolidColorBrush(Colors.Red);
}

Si la longitud de la contraseña es inferior a tres carácteres, se devuelve color rojo, si es ingerior a seis carácteres, color naranja, devolviendo color verde en caso de que la longitud de la contraseña sea superior a seis carácteres.

El segundo de los métodos:

public double PasswordStrengthValue(string password)
{
     if (string.IsNullOrEmpty(password))
          return 0;
 
     int length = password.Length;
 
     if (length >= 3 && length < 6)
          return 66;
     if (length >= 6)
          return 100;
 
     return 33;
}

Aumentará el progreso de la barra de progreso en base a la longitud de la contraseña.

El resultado:

Function Bindings
Function Bindings

En un escenario donde se necesite un enlace a datos en modo two-way, se debe tener un segundo método encargado de hacer la operación inversa. Esto se consigue utilizando la propiedad BindBack.

Conversiones implícitas

x:Bind añade soporte a conversiones implícitas de algunos tipos.

¿Cuántas veces has creado un BoolToVisibilityConverter o similar (inverso, con opacidad, etc.)?.

Ya no sería necesario!. Podemos tener propiedades de tipo bool:

public bool IsVisible { get; set; }
public bool IsNotVisible { get; set; }

Donde una es cierta y otra falsa:

IsVisible = true;
IsNotVisible = false;

Al enlazar desde la UI:

<TextBlock 
     Text="Visible"
     Visibility="{x:Bind IsVisible}"/>
<TextBlock 
     Text="No Visible"
     Visibility="{x:Bind IsNotVisible}"/>

Podemos enlazar la propiedad Visibility directamente con bindings compilados a las propiedades de tipo bool.

¿El resultado?

Conversiones implícitas
Conversiones implícitas

El esperado, se muestra el elemento visual enlazada la propiedad verdadera. Se realiza la conversión implícita de un tipo a otro.

Castings de valor explícito soportados

Al contrario que el sistema de enlace a datos clásico que hace uso de duck typing, validación semántica, para validar el enlace, los bindings compilados verifican en tiempo de compilación que los tipos de los datos proporcionados coinciden. O lo que es lo mismo, si el valor que se estan tratando de enlazar no tiene un casting implícito al tipo de la propiedad, no se puede realizar el enlace sin un converter.

Eso era hasta ahora. Al más puro estilo C#, podemos declarar casting explícitos en un binding compilado.

Si tenemos una propiedad de tipo object:

public object Element { get; set; }

Si al crear la instancia, se crea utilizando un tipo específico (en nuestro ejemplo se utilizan tipos básicos para simplificar el ejemplo, object y TextBlock):

Element = new TextBlock
{
     Text = "Cool"
};

En la UI:

<TextBlock 
     Text="{x:Bind ((TextBlock)Element).Text}"/>

El resultado:

Casting explícito
Casting explícito

Se hace el casting al tipo TextBlock y podemos acceder a sus propiedades, enlazamos al texto.

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

Ver GitHub

Interesantes novedades, algunas de ellas muy solicitadas. También se han añadido otro tipo de actualizaciones menores como el soporte a más tipos de datos, por ejemplo, acceder a un índice específico de un diccionario clave-valor. Sin embargo, nos hemos concentrado en las que mayor impacto directo pueden tener en el desarrollo del día a día.

Y a ti, ¿qué te parecen?, ¿cúal es tu novedad favorita?, ¿qué te gustaría que se añadiese?.

Más información

[Material CartujaDotNet] Sevilla Windows REBUILD

El evento

El pasado 21 de Abril, desde el grupo CartujaDotNet, grupo de usuarios .NET de Sevilla, organizábamos el Sevilla Windows REBUILD. Un evento donde recopilamos algunas de las principales novedades a nivel de desarrollo en la plataforma Universal Windows además de tener momentos para el networking y algun que otro detalle con los asistentes.

Novedades desarrollo UWP presentadas en el BUILD
Novedades desarrollo UWP presentadas en el BUILD

El material

Pude participar en el evento con múltiples sesiones. Tras una breve introducción a la agenda del evento comenzamos viendo las últimas novedades introducidas en Visual Studio (VS 2015 Update 2 y VS 15) destinadas para el desarrollo de UWP. Novedades en el editor XAML o Editar y Continuar en XAML:

Continuamos utilizando Desktop App Converter (aka Project Centennial) viendo como convertir una App Win32 en UWP.

En la siguiente sesión nos centramos en el desarrollo de aplicaciones UWP para Xbox One. Vimos desde como activar cualquier Xbox One para el modo desarrollador hasta como adaptar nuestras aplicaciones para ofrecer la mejor experiencia posible (gestión del gamepad, foco, TV safe area o sonidos).

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

Ver GitHub

Quisiera terminar añadiendo algunos agradecimientos a Josué Yeray por participar junto a un servidor en las sesiones y por supuesto, muchas gracias a todos los asistentes.

Más información

[Evento CartujaDotNet] Windows //REBUILD

dateEl evento

Tras un //BUILD 2016 repleto de novedades, ¿algo mejor que tener varias sesiones con las novedades más destacadas relacionadas con el desarrollo en Windows?.

//BUILD 2016
//BUILD 2016

Desde CartujaDotNet estamos convencidos que tendremos una divertida tarde con por supuesto sesiones técnicas, pero además, regalos, networking y otras grandes sorpresas.

La agenda

Contaremos con la siguiente agenda:

  • 18:00h – 18:15h Bienvenida. Receopción y bienvenida de asistentes.
  • 18:15h – 18:30h Novedades en Visual Studio para desarrollo UWP.  ¿Desarrollas aplicaciones universales Windows?. En esta sesión veremos algunas de las últimas novedades introducidas en Visual Studio destinadas para el desarrollo de UWP. Novedades como Editar y Continuar en XAML, novedades en depuración, etc.
  • 18:30h – 19:00h Desktop App Converter: De Win32 a UWP!. Llegar a la Windows Store es ahora más asequible que nunca. Si has invertido tiempo, esfuerzo y conocimientos en el desarrollo de aplicaciones Win32, ahora utilizando Project Centennial  te explicamos como convertirla en UWP y aprovechar todas las nuevas posibilidades disponibles, nuevas APIs, la Windows Store, nuevas formas de monetización y mucho más.
  • 19:00h – 20:00h Creando aplicaciones UWP para Xbox One. Llego el momento, por fin, podemos crear aplicaciones universales windows para Xbox One. En esta sesión veremos desde como activar cualquier Xbox One para el modo desarrollador hasta como adaptar nuestras aplicaciones para ofrecer la mejor experiencia.
  • 20:00h – 21:00h Introducción al desarrollo para HoloLens. Con las herramientas, emulador y todo lo necesario, nada mejor que ver las posibilidades de desarrollo con HoloLens, ¿cierto?.

Una agenda completa repleta de grandes novedades de la mano de Josué Yeray y un servidor.

La fecha

El evento tendrá lugar el próximo Jueves, 21 de Abril de 18:00h a 21:00h. Tendremos cuatro sesiones técnicas abordando algunas de las principales novedades en el desarrollo en la plataforma Windows.

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
Av. Reina Mercedes s/n
Sevilla Se 41012

ETS de Ingeniería Informática
ETS de Ingeniería Informática

Si a pesar de todo el contenido de la agenda te parece poco, contaremos también con alguna sorpresa y regalo a sortear entre los asistentes.

¿Te apuntas?

Más información

[Windows 10] Imprimiendo desde nuestras Apps UWP

Print - 01Introducción

Con Windows 10 tenemos gran cantidad de novedades interesantes relacionadas con el desarrollo de aplicaciones empresariales. Desde el gran potencial de Continuum a novedades en la Store pasando por nuevas APIs para trabajar con datos biométricos, bluetooth, criptografía, etc. Entre todas las características que podríamos necesitar, brilla con luz propia la posibilidad de imprimir.

En este artículo vamos a centrarnos en las posibilidades disponibles en los namespaces Windows.Graphics.Printing y Windows.UI.Xaml.Printing.

Nuestra aplicación

Nuestro objetivo será crear una aplicación UWP que mostrará un listado de la compra permitiendo imprimir la misma.

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.

Comenzamos definiendo la vista de nuestro ejemplo:

<Grid>
     <Grid.RowDefinitions>
         <RowDefinition Height="*" />
         <RowDefinition Height="50" />
     </Grid.RowDefinitions>
     <StackPanel 
        x:Name="PrintableContent">
        <TextBlock 
             Text="Shopping List"
             FontWeight="Black"
             Margin="12"/>
         <ListView
             ItemsSource="{Binding Products}"/>
     </StackPanel>
     <Button 
        Grid.Row="1"
        Content="Print"
        HorizontalAlignment="Center"/>
</Grid>

Sencilla. Cuenta con un listado que mostrará el listado de productos a comprar además de un botón que tendrá como objetivo realizar la impresión de la lista. La lista se encuentra enlazada a una propiedad pública llamada Products:

private ObservableCollection<string> _products;
public ObservableCollection<string> Products
{
     get { return _products; }
     set
     {
          _products = value;
          RaisePropertyChanged();
     }
}

Una colección de cadenas que contendrá cada producto. Cargaremos una lista desde local para simplificar el ejemplo y centrar toda nuestra atención en la impresión.

private void LoadProducts()
{
     Products = new ObservableCollection<string>
     {
          "Milk",
          "Apples",
          "Water",
          "Chicken meat",
          "Coffee",
          "Yogurt",
     };
}

Si ejecutamos el ejemplo veremos:

Nuestro ejemplo
Nuestro ejemplo

Registro para imprimir

En nuestro ejemplo, donde utilizamos el patrón MVVM y otras buenas prácticas, vamos a crear un servicio de impresión lo más genérico posible con el objetivo de poder reutilizarlo de forma rápida y sencilla entre diferentes desarrollos.

El primer paso para poder imprimir desde nuestras aplicaciones pasa por el registro del contrato de impresión. Nuestra aplicación debe realizar este proceso en cada pantalla desde la que deseamos permitir imprimir. Sólo podremos registrar la vista activa por lo también debemos preocuparnos de un proceso en el que eliminar el registro. Si nuestra aplicación navega a una nueva vista de la que deseamos imprimir de una anterior registrada para la impresión, al salir de la previa debe eliminar el registro correctamente.

Para realizar las operaciones básicas de impresión entre las que se encuentra el registro debemos utilizar la clase PrintManager  disponible en el namespace Windows.Graphics.Printing. PrintManager lo utilizaremos para indicar que la aplicación desea participar en la impresión además de poder iniciar la misma programáticamente.

También utilizaremos la clase PrintDocument disponible en el namespace Windows.UI.Xaml.Printing. En esta clase contamos con los métodos y propiedades necesarias para establecer el contenido a enviar a la impresora.

Definimos dos variables con cada una de las clases anteriores en nuestro servicio:

protected PrintManager Printmgr;
protected PrintDocument PrintDoc;

Nos centrámos en el proceso de registro:

public virtual void RegisterForPrinting()
{
     PrintDoc = new PrintDocument();
     PrintDoc.Paginate += CreatePrintPreviewPages;
     PrintDoc.GetPreviewPage += GetPrintPreviewPage;
     PrintDoc.AddPages += AddPrintPages;

     Printmgr = PrintManager.GetForCurrentView();

     Printmgr.PrintTaskRequested += PrintTaskRequested;
}

Instanciamos las clases PrintManager y PrintDocument además de realizar la subscripción a los eventos necesarios.

Para realizar el registro del contrato de impresión desde nuestra ViewModel utilizando el servicio, lo inyectaremos vía constructor:

private IPrintService _printService;

public MainViewModel(IPrintService printService)
{
     _printService = printService;
}

Y utilizamos el método creado RegisterForPrinting:

_printService.RegisterForPrinting();

NOTA: Al salir de la vista debemos quitar la subscripción al contrato de impresión, es decir, todos los eventos a los que nos registramos durante el registro. Si contámos con una aplicación con múltiples vistas, sin elimimar la subscripción, si dejamos la vista y regresamos obtendremos una excepción.

El contenido a imprimir

Ya que tenemos lo suficiente para registrar el contrato de impresión, debemos establecer el contenido que deseamos imprimir. En nuestro ejemplo contámos con un listado de productos correspondientes a una lista de la compra que deseamos imprimir.

¿Cómo funciona?

protected UIElement PrintContent;

La clase UIElement es una clase base de la mayoría de elementos visuales en Windows Runtime. Podemos utilizar cualquier elemento visual derivado de esta clase para imprimir. Nuestro contenido de impresión será por lo tanto un UIElement.

Tenemos donde almacenar el contenido a imprimir, necesitaremos también la operación de impresión. Para ello, utilizaremos una instancia de la clase PrintTask, la operación de impresión, incluyendo el contenido.

protected PrintTask Task;

Cuando realizamos el registro del contrato de impresión, nos registramos a múltiples eventos entre los que destaca PrintTaskRequested. Este evento correspondiente al PrintManager será el primero en lanzarse cuando el usuario quiera imprimir y le mostremos la ventana con la previsualización del contenido.

protected virtual void PrintTaskRequested(PrintManager sender,
            PrintTaskRequestedEventArgs e)
{
     Task = e.Request.CreatePrintTask(PrintTitle, async sourceRequested =>
     {
          PrintTaskOptionDetails printDetailedOptions = PrintTaskOptionDetails.GetFromPrintTaskOptions(Task.Options);

          printDetailedOptions.DisplayedOptions.Clear();
          printDetailedOptions.DisplayedOptions.Add(StandardPrintTaskOptions.Copies);

          Task.Options.Orientation = PrintOrientation.Portrait;

          Task.Completed += async (s, args) =>
          {
               if (args.Completion == PrintTaskCompletion.Failed)
               {
                    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                         Debug.WriteLine("Failed to print.");
                    });
               }
           };

           await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
           {
                sourceRequested.SetSource(PrintDoc?.DocumentSource);
           });
     });
}

En este evento, instanciamos PrintTask utilizando el método PrintTaskRequest.CreatePrintTask.

NOTA: Le podemos pasar una cadena de texto al método CreatePrintTask. Se mostrará el texto en el título de la ventana de previsualización del contenido a imprimir.

Tras crear la tarea de impresión, se verifica el número de páginas a imprimir. Se lanza el evento Paginate correspondiente a la interfaz IPrintPreviewPageCollection.

protected virtual void CreatePrintPreviewPages(object sender, PaginateEventArgs e)
{
     PrintDoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
}

Este evento puede lanzarse en múltiples ocasiones. Cada vez que el usuario modifique parámetros de configuración en la vista de previsualización de la impresión, el evento se lanzará para ajustar el contenido.

NOTA: Hemos simplificado nuestro ejemplo añadiendo directamente una única página. Podemos definir y crear tantas páginas como sea necesario.

Cuando en la vista de previsualización se va a mostrar una página, se lanza el evento GetPreviewPage.

protected virtual void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
{
     PrintDocument printDoc = (PrintDocument)sender;
     printDoc.SetPreviewPage(e.PageNumber, PrintContent);
}

Sencillamente establecemos el contenido de previsualización al PrintDocument.

Por último, contámos con el evento AddPages del conjunto de eventos al que nos registramos al registrar el contrato de impresión.

protected virtual void AddPrintPages(object sender, AddPagesEventArgs e)
{
     PrintDoc.AddPage(PrintContent);

     PrintDoc.AddPagesComplete();
}

En este evento añadimos el contenido a añadir a la colección de páginas de PrintDocument, es decir, el contenido a enviar a la impresora.

Estableciendo el contenido

Tras repasar cada unos de los eventos que componen el proceso de impresión, ¿cómo añadimos contenido desde nuestra ViewModel?. En nuestro servicio:

public virtual void SetPrintContent(UIElement content)
{
     if (content == null)
          return;

     PrintContent = content;
}

Tenemos un sencillo método que nos permite establecer cualquier elemento de la interfaz derivado de UIElement como contenido.

Añadir contenido desde la ViewModel será muy sencillo:

_printService.SetPrintContent(GetPrintableContent());

Para añadir el contenido utilizamos un Helper que nos permite obtener un elemento específico de UI utilizando su nombre:

private UIElement GetPrintableContent()
{
     return VisualHelper.FindChild<UIElement>(
                Window.Current.Content, "PrintableContent");
}

El Helper hace uso internamente de la clase VisualTreeHelper.

Mostrar previsualización

Con el registro del contrato de impresión, preparación del contenido y todo lo necesario para poder imprimir, es hora de permitir la operación al usuario. En nuestr servicio, utilizaremos el método asíncrono ShowPrintUIAsync para mostrar una ventana modal al usuario con la previsualización del contenido, las opciones de impresión además de por supusto las opciones para completar o cancelar la impresión.

public async void ShowPrintUiAsync(string title)
{
     try
     {
          PrintTitle = title;

          await PrintManager.ShowPrintUIAsync();
     }
     catch (Exception e)
     {
          Debug.WriteLine("Error printing: " + e.Message + ", hr=" + e.HResult);
     }
}

Si en el momento de lanzar el método ShowPrintUIAsync la operación no es posible, se lanzará una excepción. Es importante capturar esta excepción y actuar en consecuencia, notificando al usuario.

Desde la ViewModel, utilizando nuestro servicio:

_printService.ShowPrintUiAsync("Print Sample");

La ventana de previsualización:

Previsualización de la impresión
Previsualización de la impresión

Además de la previsualización, podremos configurar las opciones de configuración. En estas opciones, además de configuraciones básicas como la impresora a elegir o el número de copias, podemos configurar orientación, modos de color y muchas otras opciones.

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] Biometría de huellas dactilares

Finger PrintIntroducción

En muchos desarrollos móviles tenemos la necesidad de contar con seguridad para limitar su acceso. Cuando hablamos de seguridad lo primero que se nos viene a la mente es la autenticación. La autenticación habitual, es decir, con usuario y contraseña, requieren una serie de características como gestión de las credenciales, complejidad de la contraseña, recuperación, etc.

Con Windows 10 tenemos dos nuevas tecnologías destinidas a facilitar la gestión de la seguridad tanto para usuarios como para desarrolladores integrando la tecnologías en sus Apps. Estas tecnologías son:

  • Windows Hello
  • Microsoft Passport

Windows Hello

El primero de ellos es un framework biométrico que soporta:

  • Reconocimiento facial
  • Escaneo de Iris
  • Huella

NOTA: Windows Hello requiere de hardware específico como lector de huellas o sensor

Microsoft Passport

Microsoft Passport aporta un mecanismo de autenticación que reemplaza las clásicas contraseñas por una combinación del dispositivo  junto a un PIN definido por el usuario o bien datos biométricos aportados por Windows Hello.

Datos biométricos, uso de huellas dactilares

Nuestro objetivo en este artículo será revisar todas las posibilidades brindadas por UserConsentVerifier. Esta nueva clase introducida en Windows 10 nos permite comprobar la disponibilidad de dispositivo que permita realizar una verificación biométrica (huella dactilar).

En nuestro ejemplo, contaremos con una interfaz sencilla:

<StackPanel
     HorizontalAlignment="Center"
     VerticalAlignment="Center">
     <Button 
          Content="Check UserConsent Availability" 
          Command="{Binding AvailabilityCommand}" />
     <TextBlock 
          Text="Request UserConsent Verification"
          Margin="0,24,0,0"/>
     <TextBox 
          PlaceholderText="Insert Message..."
          Text="{Binding Message, Mode=TwoWay}"/>
     <Button 
          Content="Request UserConsent Verification" 
          Command="{Binding VerificationCommand}"
          IsEnabled="{Binding IsUserConsentAvalaible}"/>
</StackPanel>

Un primer botón que nos indique la disponibilidad de dispositivo que permita realizar una verificación biométrica mientras que el segundo nos permitirá realizar la verificación del usuario con los datos biométricos.

Ambos botones contarán con un comando asociado en la ViewModel. El primero:

private ICommand _availabilityCommand;       

public ICommand AvailabilityCommand
{
     get { return _availabilityCommand = _availabilityCommand ?? new DelegateCommandAsync(AvailabilityCommandExecute); }
}

public async Task AvailabilityCommandExecute()
{

}

Y el segundo:

private ICommand _verificationCommand;

public ICommand VerificationCommand
{
     get { return _verificationCommand = _verificationCommand ?? new DelegateCommandAsync(VerificationCommandExecute); }
}

public async Task VerificationCommandExecute()
{

}

Nada especial a destacar llegados aquí. Continuamos…

Detectar si contamos con lector de huellas

Para poder utilizar datos biométricos en nuestra aplicación debemos antes que nada verificar si el dispositivo cuenta con lector de huellas. Podemos utilizar el método UserConsentVerifier.CheckAvailabilityAsync para la verificación.

En el servicio:

public async Task<UserConsentVerifierAvailability> GetUserConsentAvailabilityAsync()
{
     UserConsentVerifierAvailability consentAvailability =
     await UserConsentVerifier.CheckAvailabilityAsync();
     
     return consentAvailability;
}

El resultado será un valor de la enumeración UserConsentVerifierAvailability. Los posibles valores de la enumeración son:

  • Available: Dispositivo de verificación biométrica disponible.
  • DeviceBusy: El dispositivo de verificación biométrica está realizando una operación y no está disponible.
  • DeviceNorPresent: No hay dispositivo de verificación biométrica.
  • DisabledByPolicy: Por directiva de grupo, la verificación biométrica esta deshabilitada.
  • NotConfiguredForUser: No hay dispositivo de verificación biométrica configurado por el usuario actual.

Desde la viewmodel de la aplicación utilizaremos el servicio para la verificación:

IsUserConsentAvalaible = await _userConsentVerifierService.CheckUserConsentAvailabilityAsync();

Hasta este punto tenemos lo necesario para detectar si contámos con la posibilidad de usar datos biométricos o no.

Solicitud de consentimiento

Llega el momento de usar datos biométricos. Tras verificar la disponibilidad del lector debemos pedir consentimiento utilizando el método UserConsentVerifier.RequestVerificationAsync.

En nuestro servicio:

public async Task<UserConsentVerificationResult> GetRequestUserConsentVerificationAsync(string message)
{
     UserConsentVerificationResult consentResult =
     await UserConsentVerifier.RequestVerificationAsync(message);

     return consentResult;
}

NOTA: Para que la autenticación con la huella funcione correctamente, el usuario debe habe registrado su huella previamente.

Desde la viewmodel, bastará con usar el método anterior:

var result = await _userConsentVerifierService.GetRequestUserConsentVerificationAsync(Message);

Como parámetro pasamos una cadena. Esta cadena se utilizará para mostrar un mensaje al usuario en la solicitud de comprobación biométrica. En cuanto al resultado, obtenemos un valor de la enumeración UserConsentVerificationResult:

switch(result)
{
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.Canceled:
          message = "Consent request prompt was canceled.";
          break;
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.DeviceBusy:
          message = "Biometric device is busy.";
          break;
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.DeviceNotPresent:
          message = "Biometric device not found.";
          break;
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.DisabledByPolicy:
          message = "Disabled by policy.";
          break;
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.NotConfiguredForUser:
          message = "No fingeprints registered.";
          break;
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.RetriesExhausted:
          message = "Too many retries.";
          break;
     case Windows.Security.Credentials.UI.UserConsentVerificationResult.Verified:
          message = "User verified.";
          break;
}

Los valores de la enumeración son:

  • Verified: Huella comprobada correctamente.
  • DeviceNotPresent: Dispositivo biométrico no disponible.
  • NotConfiguredForUser: No hay ningún dispositivo biométrico configurado por el usuario actualo.
  • DisableByPolicy: La directiva de grupo ha deshabilitado el dispositivo biométrico.
  • DeviceBusy: Dispositivo biométrico ocupado al encontrarse realizando una operación.
  • RetriesExhausted: Tras 10 intentos, la solicitud de comprobación original y todos los intentos han fallado.
  • Canceled: Cancelación de la operación.

Sencillo, ¿cierto?.

Si ejecutamos la aplicación en un Lumia con opción biométrica, como por ejemplo un Lumia 950XL, ofrecerá dos opciones de auenticación:

  • Escáner de Iris
  • Código PIN

Igual resultado obtendremos en un PC que cuente con RealSense. Sin embargo, si probamos en un dispositivo sin posibilidad de detección biométrica aun así nos permitirá la validación vía código PIN.

PIN Code
PIN Code

De forma evidente no es un mecanismo de autenticación biométrica pero si suficiente para cubrir las necesidades de la API.

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

[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