[Tips and Tricks] Universal Apps. Behavior para gestionar gestos swipe con comando

SwipeIntroducción

En las aplicaciones móviles la pantalla táctil permite una interacción profunda del usuario con la misma. Gracias a la pantalla táctil el usuario puede interactuar utilizando gran variedad de gestos. Tocar, un toque prolongado, deslizar, pinzamientos y asi cubriendo una gran variedad de gestos con uno o varios dedos.

En aplicaciones Windows Phone 8.1 Silverlight asi como en aplicaciones Universales WinRT tenemos a nuestra disposición un conjunto de eventos de manipulación que nos permiten gestionar la actividad dada en la pantalla táctil con uno o más dedos. Podemos determinar toques, toques prolongados, deslizamientos, etc. Sin embargo, la gestión es mediante eventos, ¿que ocurre si deseamos una gestión igual de sencilla pero manteniendo nuestra arquitectura sólida basada en MVVM?

En este artículo vamos a ver como crear un Behavior que nos permita capturar un tipo de gestos e invocar un comando de nuestra ViewModel.

¿Te apuntas?

Crear el Behavior

Comenzamos creando un nuevo proyecto:

Nueva Aplicación Universal

Nueva Aplicación Universal

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.

Nuestro objetivo sera muy sencillo. Nuestra aplicación de ejemplo tendra un Behavior asociado al Grid principal que capturará los gestos de tipo Swipe lanzando un comando de la ViewModel pasando como parámetro la dirección del gesto.

NOTA: El Behavior se podrá asociar a cualquier elemento derivado de UIElement.

Los Behaviors nos permiten añadir lógica a nuestro XAML para realizar acciones sencillas (pero increíblemente útiles). Entre el conjunto variado de acciones que nos permiten ejecutar, podemos lanzar animaciones, cambiar el estado de un VisualState, realizar la navegación de páginas o invocar un comando. En líneas generales suelen permitir realizar tareas habituales sin necesidad de escribir código en el code-behind de la vista lo que hace posible que mantengamos una arquitectura sólida sin código auxiliar repartido en distintas partes.

Añadir extensión Behaviors

Añadir extensión Behaviors

Con la llegada del SDK de Windows 8.1 recibimos también la incorporación del Behavior SDK. Disponible en una librería aparte del Core de .NET, podemos utilizarlo facilmente añadiendo la referencia a la misma.

NOTA: Con la llegada de Windows Phone 8.1 recibimos acceso a la misma extensión.

Si para nuestros objetivos, ninguno de los Behaviors que tenemos disponible nos cubre, podemos crear Behaviors nuevos totalmente personalizados.

A diferencia de la implementación que teníamos en WPF por ejemplo, no contamos con clases del tipo Behavior<T> de las que derivar, sino que contamos con interfaces, IBehavior. Disponible en el namespace Microsoft.Xaml.Interactivity.

public abstract class Behavior<T> : DependencyObject, IBehavior
       where T : DependencyObject
{

     public T AssociatedObject { get; set; }

     protected virtual void OnAttached()
     {
     }

     protected virtual void OnDetaching()
     {
     }

     public void Attach(DependencyObject associatedObject)
     {
            if (associatedObject == this.AssociatedObject ||
                DesignMode.DesignModeEnabled)
            {
                return;
            }

            this.AssociatedObject = (T)associatedObject;
            OnAttached();
     }

     public void Detach()
     {
            if (!DesignMode.DesignModeEnabled)
            {
                OnDetaching();
            }
     }

     DependencyObject IBehavior.AssociatedObject
     {
            get { return this.AssociatedObject; }
     }
}

La clase superior implementa la interfaz IBehavior de modo que facilmente podemos sobreescribir la propiedad AssociatedObject. Creamos un nuevo Behavior para los elementos que deriven de UIElement:

public class SwipeBehavior : Behavior<UIElement>
{

}

Implementamos la interfaz:

protected override void OnAttached()
{
     base.OnAttached();

     this.AssociatedObject.ManipulationMode =
                this.AssociatedObject.ManipulationMode |
                ManipulationModes.TranslateX |
                ManipulationModes.TranslateY;

     this.AssociatedObject.ManipulationCompleted += OnManipulationCompleted;
}

protected override void OnDetaching()
{
     base.OnDetaching();

     this.AssociatedObject.ManipulationCompleted -= OnManipulationCompleted;
}

private void OnManipulationCompleted(object sender,
            ManipulationCompletedRoutedEventArgs e)
{

}

SwipeBehavior hereda de la clase Behavior<T>.  En el evento sobreescrito OnAttached nos suscribimos al evento ManipulationCompleted gestionando los modos de trasladación en el eje X e Y (Swipes). En el método OnDetaching realizamos la dessuscripción del evento ManipulationCompleted.

public ICommand SwipeCommand
{
     get { return (ICommand)GetValue(SwipeCommandProperty); }
     set { SetValue(SwipeCommandProperty, value); }
}

public static readonly DependencyProperty SwipeCommandProperty =
     DependencyProperty.Register("SwipeCommand",
     typeof(ICommand),    
     typeof(SwipeBehavior),  
     new PropertyMetadata(null));

public static ICommand GetSwipeCommand(DependencyObject d)
{
     return (ICommand)d.GetValue(SwipeCommandProperty);
}

Creamos una DependencyProperty de tipo ICommand. De esta forma, permitimos bindear un comando a ejecutar pasándole como parámetro la dirección del gesto.

Gestión de gestos

Verificaremos valores recibidos de ManipulationCompletedRoutedEventArgs. Concretamente haremos varias verificaciones tanto en el eje X como en el Y para determinar la dirección del gesto. Creamos dos sencillas constantes para simplificar los cálculos:

private const double Min = 0.5;
private const double Max = 100;

En el evento ManipulationCompleted:

bool right = e.Velocities.Linear.X.CompareTo(Min) >= 0 && e.Velocities.Linear.X.CompareTo(Max) <= 0;
bool left = e.Velocities.Linear.X.CompareTo(-Max) >= 0 && e.Velocities.Linear.X.CompareTo(-Min) <= 0;
bool up = e.Velocities.Linear.Y.CompareTo(-Max) >= 0 && e.Velocities.Linear.Y.CompareTo(-Min) <= 0;
bool down = e.Velocities.Linear.Y.CompareTo(Min) >= 0 && e.Velocities.Linear.Y.CompareTo(Max) <= 0;

var swipeCommand = GetSwipeCommand(this);

if (swipeCommand != null)
{
     if (right && !(up || down))
                    swipeCommand.Execute(SwipeDirection.Right);
     if (left && !(up || down))
                    swipeCommand.Execute(SwipeDirection.Left);
     if (up && !(right || left))
                    swipeCommand.Execute(SwipeDirection.Up);
     if (down && !(right || left))
                    swipeCommand.Execute(SwipeDirection.Down);
}

El argumento de tipo ManipulationCompletedRoutedEventArgs, cuenta con toda la información relacionada con la manipulación realizada (gesto). Entre el conjunto de propiedades disponibles contamos con Velocities.Linear.X y Velocities.Linear.Y que nos permiten determinar la dirección del gesto. Dependiendo de la dirección del gesto, ejecutaremos el comando bindeado en la propiedad SwipeCommand pasándole como parámetro la dirección.

En nuestra interfaz, detectaremos el gesto de tipo Swipe y segun la orientación del gesto, mostrando un texto de información indicándola.

En nuestra ViewModel, definiremos la propiedad que mostrará en pantalla la orientación del gesto:

private string _info;

public string Info
{
     get { return _info; }
     set
     {
          _info = value;
          RaisePropertyChanged();
     }
}

Al lanzar un gesto de tipo Swipe lanzaremos un comando:

private ICommand _swipeCommand;

public ICommand SwipeCommand
{
     get { return _swipeCommand = _swipeCommand ?? new DelegateCommand<SwipeDirection>(SwipeCommandExecute); }
}

private void SwipeCommandExecute(SwipeDirection direction)
{
     switch (direction)
     {
          case SwipeDirection.Up:
               Info = "Arriba";
               break;
          case SwipeDirection.Down:
               Info = "Abajo";
               break;
          case SwipeDirection.Left:
               Info = "Izquierda";
               break;
          case SwipeDirection.Right:
               Info = "Derecha";
               break;
     }
}

Recibimos como parámetro el tipo del Swipe de modo que podemos ejecutar en consecuencia.

En la interfaz de usuario, ante cualquier elemento visual derivado de UIElement:

<interactivity:Interaction.Behaviors>
     <behaviors:SwipeBehavior
          SwipeCommand="{Binding SwipeCommand}" />
</interactivity:Interaction.Behaviors>

Sencillo, ¿verdad?

GesturesBehavior

GesturesBehavior

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

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

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

[Evento WPSUG] Porqué no has terminado, cuando has terminado tu app

MoneyIntroducción

Nuestro trabajo como desarrolladores de Apps móviles no termina al publicar la App en la Store. De hecho, podríamos decir que ahi comienza un verdadero trabajo de análisis, recopilación de información y diferentes estrategias de Marketing con el fin de potenciar a la misma.

wpsug.png-550x0

El evento

El próximo Jueves 26 de Marzo, a las 19:00 (GMT+1)  tendra lugar un Hangout en el que tendremos el placer de contar con Vanessa Estorach, con quien veremos diferentes técnicas y estrategias de Marketing en Apps móviles, destacar la App, optimizar resultados, aumentar beneficios, etc.

  • 19:00 en España
  • 13:00 en Colombia
  • 12:00 en México Centro
  • 13:30 en Venezuela
  • 15:00 en Chile continental

¿Te apuntas?

Más información

[Tips and Tricks] Universal Apps. Evento ItemClick a Comando

List All Application installed on cmputerEl problema

Los controles ListView y GridView son una pieza angular muy importante en la mayoría de aplicaciones Windows Store y Windows Phone. En su uso necesitaremos determinar cuando el usuario selecciona un elemento. Trabajando con el patrón MVVM nos encontramos con un problema. Para determinar el elemento seleccionado tenemos dos opciones:

  1. Utilizar la propiedad SelectedItem. Podemos bindear una propiedad de nuestra ViewModel a la propiedad SelectedItem del control. Es una opción válida pero en ciertas situaciones puede no ser completa. Por ejemplo, si al establecer la propiedad realizamos una navegación, al volver atrás si volvemos a intentar navegar al mismo elemento no ocurrira nada. Esto es asi, porque la propiedad SelectedItem esta ya establecida y no cambia. Debemos establecer a nulo la propiedad utilizando los eventos de navegación.
  2. Utilizar el evento ItemClick que nos facilita un argumento de tipo ItemClickEventArgs con la información del elemento seleccionado. Forma idónea pero con un gran problema, no expone una propiedad Command para bindear y utilizar nuestra ViewModel.

¿Qué podemos hacer?

En este artículo crearemos una sencilla clase que exponga una propiedad de dependencia adjunta a utilizar.

ItemClick utilizando un Comando

Añadiremos un GridView con su propiedad ItemsSource bindeada a una propiedad de la ViewModel:

<GridView
     ItemsSource="{Binding Items}"
     SelectionMode="None"
     IsSwipeEnabled="false"
     IsItemClickEnabled="True" />

Deshabilitamos los modos de selección y habilitamos el evento ItemClick. En la ViewModel tendremos definida la propiedad:

private ObservableCollection<string> _items;

public ObservableCollection<string> Items
{
     get
     {
          if (_items == null)
             LoadData();
         return _items;
     }
     set { _items = value; }
}

La propiedad la instanciará un método LoadData que rellenará la colección de sencillos datos para poder probar:

private void LoadData()
{
     _items = new ObservableCollection<string>();
     for (int i = 0; i < 100; i++)
     {
          _items.Add((i + 1).ToString());
     }
}

Llega el momento clave. Vamos a crear una clase donde definiremos una propiedad de dependencia adjunta (Attached DependencyProperty) de tipo ICommand:

public static class ItemClickCommandBehavior
{
     public static readonly DependencyProperty ItemClickCommandProperty =
            DependencyProperty.RegisterAttached("ItemClickCommand", typeof(ICommand),
            typeof(ItemClickCommandBehavior), new PropertyMetadata(null, OnItemClickCommandPropertyChanged));

     public static void SetItemClickCommand(DependencyObject d, ICommand value)
     {
         d.SetValue(ItemClickCommandProperty, value);
     }

     public static ICommand GetItemClickCommand(DependencyObject d)
     {
         return (ICommand)d.GetValue(ItemClickCommandProperty);
     }

     private static void OnItemClickCommandPropertyChanged(DependencyObject d,
         DependencyPropertyChangedEventArgs e)
     {
         var control = d as ListViewBase;
         if (control != null)
             control.ItemClick += OnItemClick;
     }

     private static void OnItemClick(object sender, ItemClickEventArgs e)
     {
         var control = sender as ListViewBase;
         var itemClickCommand = GetItemClickCommand(control);

         if itemClickCommand != null && command.CanExecute(e.ClickedItem))
             itemClickCommand.Execute(e.ClickedItem);
     }
}

Cuando se ejecute el evento ItemClick, se tomará el Comando definido en la propiedad, se le pasará el argumento ClickedItem y se ejecutará.

NOTA: Importante resaltar que en la clase utilizamos ListViewBase, por lo que podremos adjuntar la propiedad tanto en controles GridView como ListView.

Ya podremos adjuntar la propiedad en nuestro GridView. Debemos definir el comando a ejecutar en la ViewModel:

private ICommand _clickCommand;

public ICommand ClickCommand
{
     get { return _clickCommand = _clickCommand ?? new DelegateCommandAsync<string>(ClickCommandExecute); }
}

private async Task ClickCommandExecute(string parameter)
{
     await _dialogService.ShowAsync(parameter);
}

El comando es sencillo. En el método Execute del mismo recibimos el objeto que contiene al elemento seleleccionado como parámetro. En nuestro ejemplo, utilizamos un sencillo servicio de dialogo para mostrar un mensaje con la información del elemento seleccionado.

Todo listo!. Solo nos queda añadir la propiedad adjunta. Añadimos en la vista el namespace donde hemos definido nuestra clase ItemClickCommandBehavior:

xmlns:behaviors="using:ItemClickCommandBehavior.Behaviors"

Y en el GridView:

behaviors:ItemClickCommandBehavior.ItemClickCommand="{Binding ClickCommand}"

De esta forma, tendremos nuestro GridView:

Nuestra colección de elementos

Nuestra colección de elementos

Al pulsar sobre cualquier elemento, nuestra clase dispará el evento ItemClick donde se tomará el comando adjunto y se ejecutará:

Comando ejecutado

Comando ejecutado

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

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

Ver GitHub

Recordar que cualquier tipo de duda o sugerencia la podéis dejar en los comentarios de la entrada.

Más información

[Windows 10] Novedades a nivel de desarrollo presentadas en el MWC15

Un primer vistazo a Apps Universales en Windows 10

Windows 10 ha llegado como la culminación en el viaje hacia la convergencia en el desarrollo entre plataformas Windows.

Gracias a esta convergencia, podemos ejecutar nuestra App en teléfonos, tabletas, PCs y en la Xbox One. Además, debemos unir los nuevos dispositivos que se unen a la «familia» como dispositivos IoT como la Raspberry Pi 2.

Universal Apps

Universal Apps

Ahora hablamos de Apps Universales escritas una única vez con un código comun tanto para la lógica de negocio como para la interfaz de usuario. Además, generamos un único paquete que mantendrá una interfaz consistente y familiar para el usuario pero adaptada a cada plataforma.

Novedades

Para constuir Apps Universales bajo un mismo paquete, con el mismo código pero adaptando la experiencia a cada plataforma se han añadido una serie de novedades a destacar.

UX Adaptable

El modelo de Apps ha sido mejorado en líneas generales pero además se ha mejorado mucho el ViewStateMananger para permitir crear vistas que se adapten al dispositivo con facilidad. Esto nos permitirá crear vistas comunes sin separar en ficheros diferentes por plataforma en la mayoría de casos. Aunque se permitirá seguir dividiendo vistas en caso necesario.

VisualStateManager

VisualStateManager

Además los controles en tiempo de ejecución se adaptarán a las condiciones usadas para interactuar por el usuario. De esta forma, tendremos controles que cambien tamaño u opciones dependiendo de si se usa entrada táctil o ratón por ejemplo.

Cortana

La integración de Cortana será mayor permitiendo búsqueda de Apps, lanzar Apps más frecuentes, etc.

Cloud

Seguiremos contando con opciones en Azure potentes y versátiles como Azure Mobile Services o el Hub de notificaciones además del servicio de notificaciones Windows (WNS), etc. Con Windows 10 llegan más servicios o algunos serán mejorados como OneDrive o Cortana.

Entrando en materia

En el Hall 8 del MWC, Microsoft puso a disposición del público una gran cantidad de Surfaces preparadas para permitir probar y crear Apps Universales Windows 10 mediante dos Hands on Labs guiados.

Devs creando Apps Universales Windows 10

Devs creando Apps Universales Windows 10

Tras probar a fondo todo lo posible, a nivel de desarrollo me gustaría destacar una serie de novedades bastante suculentas.

RelativePanel

Estamos ante un nuevo Panel que tiene como principal objetivo permitir crear interfaces con diseños que se adapten con facilidad a cualquier tipo de tamaño. Posiciona a los elementos que contiene de manera relativa entre ellos.

Veamos un ejemplo sencillo:

<RelativePanel>
    <TextBlock x:Name="MyTextBlock" Text="TextBlock 1" RelativePanel.AlightHorizontalCenterWithPanel="True" />
    <TextBlock Text="TextBlock 2" RelativePanel.RightOf="{Binding ElementName=MyTextBlock}" />
</RelativePanel>

En el trozo de XAML de la parte superior utilizamos el nuevo Panel para posicionar dos simples textos. El primero de los textos se situará de manera central en forma horizontal al Panel. El segundo es mucho más interesante. Su posición será a la derecha del primer texto.

Tendremos disponible una gran variedad de opciones de alineación que serán las que otorgen un potencial muy alto al Panel.

Opciones de alineación del RelativePanel

Opciones de alineación del RelativePanel

Además, podremos utilizar StateTriggers para modificar la relación entre controles segun ciertas condiciones.

SplitView

Para que nos situemos de entrada, muchos conocéis alternativas visuales similares como «Hamburger menu». Crea un menu deslizante lateral.

SplitView

SplitView

Es impresionantemente sencillo de utilizar. Cuenta con una propiedad Pane que permite establecer el contenido del panel. Podemos mostrar el panel a izquierda o derecha mediante la propiedad PanePlacement y el ancho utilizando OpenPaneLength.

Veamos un sencillo ejemplo:

<SplitView
    x:Name="SplitView"
        OpenPaneLength="200"
        Background="Red"
        PanePlacement="Left">
        <SplitView.Pane>
            <Grid>
                <TextBlock Text="SplitView Pane" />
            </Grid>
        </SplitView.Pane>
        <Grid>
            <TextBlock Text="Content" />
        </Grid>
</SplitView>

Por último, tenemos la propiedad IsPaneOpen para determinar si el menu esta abierto o cerrado.

StateTriggers

Llegamos a una de las novedades más potentes. Hasta ahora utilizábamos ya Visual States para gestionar la apariencia de la interfaz de Apps WinRT.

Con una clara inspiración en los triggers de WPF nos llegan los StateTriggers. Su uso es muy simple, realizar un cambio en un elemento visual cuando se cumpla una condición.

Actualmente tenemos disponible AdaptiveTrigger. Basicamente la condición depende de un ancho y alto mínimo que establecemos con las propiedades MinWindowHeight y MinWindowWidth. Cuando la ventana supera el tamaño, el VisualState se activa.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
        <VisualState x:Name="WideState">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="600" />
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="MainGrid.Margin" Value="15, 50" />
            </VisualState.Setters>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

¿Os recuerda un poco a las Media Queries en CSS?. Muchos Devs de los que probaron el SDK en alguno de los Hands On Labs me lo comentaron con frecuencia.

Tenemos otra serie de novedades en emuladores, herramientas, etc. que comentaremos con calma cuando Microsoft lo muestre con más detalles.

¿Qué es lo próximo?

Podríamos resumirlo con suma facilidad, //BUILD. Lo mostrado en este MWC es sin duda un gran adelanto que nos abre el apetito vorazmente dejandonos con ansias de mucho más. Se denota un gran esfuerzo por mejorar las herramientas de desarrollo, mejorar la plataforma y sobretodo mejorar las opciones para utilizar y compartir código entre plataformas.

Nos vemos en la próxima!

Nos vemos en la próxima!

Mientras llega la fecha, os recomiendo apuntaros al programa Insider si aun no lo hicisteis y en el caso de desarrollo, la mejor recomendación por ahora es crear Apps Universales 8.1 que serán una gran base de partida cuando la nueva plataforma este disponible.

En el próximo //BUILD se mostrará más, se darán más detalles, etc. Asi que, no se vosotros pero…¿a llegado ya el BUILD?…¿y ahora?….¿y ahora?

Más información

[Tips and Tricks] RequestTheme, cambiar el tema al vuelo!

ColorIntroducción

En Windows Phone, podemos definir un tema totalmente personalizado para nuestras aplicaciones. Por defecto, ya contamos con dos temas, claro y oscuro («Light» y «Dark» respectivamente). Si no usamos nuestro propio tema totalmente personalizado debemos gestionar los elementos de nuestra interfaz para una correcta visualización tanto en el tema claro como en el oscuro. El usuario puede cambiar el tema a utilizar (por defecto oscuro).

Podemos forzar el utilizar un tema en concreto e incluso en tiempo de ejecución podemos modificar el tema a utilizar para cualquier elemento.

NOTA: Modificar la propiedad Application.RequestedTheme en tiempo de ejecución provoca una excepción.

RequestTheme

La propiedad RequestedTheme nos permite obtener o establecer el tema a utilizar. Al cambiar el valor de RequestedTheme de la Aplicación a nivel general o de un elemento en concreto provoca que se obtengan las plantillas y estilos del ResourceDictionary denominados «Light» o «Dark» de la colección ThemeDictionaries.

NOTA: La propiedad RequestedTheme es ignorada si el usuario tiene activo el modo de alto contraste.

Crearemos un ejemplo muy simple para realizar el cambio en tiempo de ejecución del tema. Creamos dos sencillos botones:

<Button Content="Light" />
<Button Content="Dark" />

El primero cambiara al tema claro y el segundo al tema oscuro:

Nuestra UI en el tema por defecto

Nuestra UI en el tema por defecto

En nuestra viewmodel, contaremos con un comando para cada botón:

private ICommand _lightCommand;
private ICommand _darkCommand;

public ICommand LightCommand
{
     get { return _lightCommand = _lightCommand ?? new DelegateCommand(LightCommandExecute); }
}

public ICommand DarkCommand
{
     get { return _darkCommand = _darkCommand ?? new DelegateCommand(DarkCommandExecute); }
}

private void LightCommandExecute()
{

}

private void DarkCommandExecute()
{

}

La definición de los botones quedara:

<Button Content="Light" Command="{Binding LightCommand}" />
<Button Content="Dark" Command="{Binding DarkCommand}" />

En Windows Phone 8.1, la App cuenta con una Window que contiene un único Frame que ocupa el 100% del área de la pantalla.

El Frame de la Aplicación

El Frame de la Aplicación

El Frame, se crea en el arranque de la aplicación y es el encargado de contener y gestionar cada una de las páginas (Page). La propiedad RequestedTheme establecida en un elemento FrameworkElement es heredado en sus elementos hijos. Podemos anular la herencia estableciendo la propiedad RequestedTheme en un elemento hijo concreto. Estableceremos la propiedad en el Frame de la Aplicación provocando que la misma sea heredada por sus elementos hijos, es decir, las páginas de la Aplicación:

AppFrame.RequestedTheme = ElementTheme.Light;

En el segundo comando establecemos el RequestedTheme del Frame de la Aplicación al tema oscuro:

AppFrame.RequestedTheme = ElementTheme.Dark;

Al cambiar al tema claro nuestra interfaz se vera de la siguiente forma:

Cambio de tema a claro

Cambio de tema a claro

El resultado en video:

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

Más información

[Christmas Mobile Apps Sessions] Material de «Publicación de Universal Apps»

DownloadEl evento

El pasado Miércoles 10 de Diciembre tenía lugar el cuarto webcast de las Christmas Mobile Apps Sessiones. Las Christmas Mobile Apps Sessions son un conjunto 10 webcasts sobre desarrollo de apps móviles con variada  temática.

En esta sesión, vimos formas de monetización en aplicaciones universales viendo como crear Trials, In-App Purchase o como añadir publicidad, creamos el paquete de aplicación, publicamos en la tienda con diferentes opciones y finalmente vimos como añadir analíticas avanzadas.

El material

La presentación utilizada:

Podéis descargar el conjunto de ejemplos realizados en el webcast a continuación:

Por último, podéis ver en cualquier momento el webcast en el siguiente video:

Más información

[Christmas Mobile Apps Sessions] Material de «Herramientas y toolkits para Universal Apps»

DownloadEl evento

El pasado Miércoles 10 de Diciembre tenía lugar el cuarto webcast de las Christmas Mobile Apps Sessiones. Las Christmas Mobile Apps Sessions son un conjunto 10 webcasts sobre desarrollo de apps móviles con variada  temática.

En esta sesión, vimos novedades de Visual Studio en XAML y C# relacionadas con aplicaciones universales, profundizamos en todas las posibilidades que nos brinda el emulador de Windows Phone, repasamos algunas características fundamentales de Blend, probamos la herramientas de análisis de rendimiento además de ver muchas otras herramientas variadas.

El material

La presentación utilizada:

Podéis descargar el conjunto de ejemplos realizados en el webcast a continuación:

Por último, podéis ver en cualquier momento el webcast en el siguiente video:

Más información

[Evento] Christmas Mobile Apps Sessions

DateEl evento

Las Christmas Mobile Apps Sessions son un conjunto 10 webcasts sobre desarrollo de apps móviles con variada  temática. Desde desarrollo de aplicaciones universales, a desarrollo de aplicaciones nativas multiplataforma con Xamarin pasando por la creación de backends.  Todo rodeado de ponentes de lujo como Josué Yeray, Santiago Porras, Rafa Serna, y un servidor, MVPs de Windows Platform Development, con Eduard Tomás, MVP de ASP.NET y con Alejandro Campos, Technical Evangelist en Microsoft.

La agenda

  1. ALM con Visual Studio Online, Jueves 4 de diciembre de 6pm a 7pm por Alejandro Campos. En esta sesión veremos cómo con Visual Studio Online podemos gestionar el ciclo de vida completo de nuestro proyecto con metodologías ágiles, así como gestionar nuestro código fuente y automatizar la compilación, las pruebas y la publicación de nuestras aplicaciones.
  2. Introducción a Universal Apps, Viernes 5 de diciembre de 6pm a 7pm por un servidor. Con la llegada de Windows Phone 8.1 al mercado los desarrolladores tenemos grandes cambios en la plataforma de desarrollo entre ellos, las aplicaciones universales. En esta sesión aprenderemos en qué consisten, como crearlas así como sus aportes. Además,  profundizaremos en la convergencia entre la plataforma Windows analizando distintas técnicas para compartir código.
  3. Buenas prácticas en Universal Apps, Martes 9 de diciembre de 6pm a 7pm por Josué YerayEn el mundo del desarrollo móvil, publicar una aplicación no es una meta, es solo un paso más del camino. Después de publicarla viene el mantenimiento y actualización, mejor cuanto más constante y rápido sea. En esta sesión vamos a ver buenas practicas que nos ayudarán a crear apps mantenibles y de fácil actualización como MVVM, Servicios, Inyección de dependencias, separación de estilos y plantillas en diccionarios…
  4. Herramientas y toolkits para Universal Apps, Miércoles 10 de diciembre de 6pm a 7pm por un servidor. Desde Visual Studio contamos con una gran cantidad de herramientas destinadas al desarrollo de aplicaciones universales. Podemos analizar paquetes, desplegar paquetes, medir el rendimiento de la aplicación y contamos con versátiles emuladores entre otras herramientas. En esta sesión repasaremos todas las herramientas incluidas con el SDK de desarrollo así como herramientas de terceros que nos incluyen librerías auxiliares y nuevos controles.
  5. Desarrollo Móvil Multi-plataforma con Xamarin, Jueves 11 de diciembre de 6pm a 7pm, por Alejandro Campos. ¿Podemos desarrollar una misma app que funcione en dispositivos Windows, Android e iOS? ¿Y que sea nativa? ¿Y podemos crearla con un mismo IDE y un mismo lenguaje? Con Visual Studio, C# y Xamarin es posible. En esta sesión te contamos cómo.
  6. Introducción al Diseño, trucos y consejos en Universal Apps, Viernes 12 de diciembre de 6pm a 7pm, por Santiago Porras. Conoce los conceptos esenciales del diseño de Aplicaciones Universales para poder crear aplicaciones que atraigan a los usuarios. Además, aprenderás algunos trucos y consejos simples para diferenciarte y crear experiencias únicas.
  7. Desarrollo Móvil Multi-plataforma con Apache Cordova, Lunes 15 de diciembre de 6pm a 7pm por Alejandro Campos. Podemos desarrollar una misma app que funcione en dispositivos Windows, Android e iOS con HTML5 y JavaScript?¿Y podemos crearla con herramientas de primer nivel que nos ayuden en la creación del código, en su depuración, y pruebas? Con Visual Studio y Apache Cordova es posible. En esta sesión te contamos cómo.
  8. Crea tu backend con Azure Mobile Services, Martes 16 de diciembre de 6pm a 7pm por Rafa Serna. Casi cualquier aplicación móvil necesita de un backend para darle toda la funcionalidad y potencia que necesita. Con Azure Mobile Services dispondremos de autenticación, servicios de datos, notificaciones y un completo set de usos, los cuales nos proporcionan un extenso backend en la nube casi a un click de distancia.
  9. Crea tu backend con ASP.NET Web API, Miércoles 17 de diciembre de 6pm a 7pm por Eduard Tomás. Da igual lo que haga tu app: tarde o temprano necesitará hablar con un servidor. En esta charla veremos cómo crear fácilmente una API REST aprovechando toda la potencia de ASP.NET WebApi y verás que da igual lo compleja que sean tus necesidades, ¡que con WebApi podrás cubrirlas rápidamente!
  10. Publicación de Universal Apps, Jueves 18 de diciembre de 6pm a 7pm por un servidor. Antes de enviar nuestra aplicación a publicar debemos revisar el archivo de manifiesto, generar y validar el paquete entre otras acciones. En esta sesión repasaremos todos los pasos necesarios a realizar antes de publicar la aplicación, repasaremos formas de monetización así como incluir analíticas y por supuesto publicaremos una aplicación universal paso a paso.

NOTA: Todas las sesiones serán grabadas para poder visualizarlas posteriormente aunque no dudéis en conectar online para aprovechar y poder realizar preguntas al ponente de turno.

Más información

[Universal App] Roaming Settings, compartiendo datos entre plataformas

SynchronizeIntroducción

Algo muy habitual en cualquier tipo de aplicación móvil es trabajar con distntos tipos de datos, datos locales de la aplicación, archivos de configuración, etc. Segun el tipo de dato debemos proporcionar un tratamiento diferente pero siempre buscando facilitar el uso de la aplicación por parte del usuario.

Ante un mundo lleno de múltiples dispositivos para distintas situaciones (PCs, Tablets, móviles, Bands, etc.) es cada vez más habitual que un mismo usuario se instale la misma aplicación en diferentes dispositivos. Ante este tipo de situación el usuario espera que los datos básicos y de configuración se mantengan sincronizados y no tener que establecer su configuración básica una y otra vez.

¿Cómo lo conseguimos?

Almacenamiento de datos de aplicación

Tanto en aplicaciones Silverlight 8.1 como en aplicaciones Windows XAML tenemos disponible las siguientes carpetas:

  • LocalFolder: Un viejo conocido. Ya lo teníamos disponible en Windows Phone 8 e incluso era la carpeta usada como Isolated Storage desde Windows Phone 7. Guardaremos información que persiste entre actualizaciones de la aplicación y entra dentro de los datos guardados al realizar un backup del sistema.
  • RoamingFolder: Almacenamiento muy útil. Al guardar datos en RoamingData, la información estará disponible en todos los dispositivos donde la aplicación este instalada (con el mismo id). Ideal para guardar la configuración de la aplicación y mantenerla sincronizada entre la aplicación Windows Phone y la Windows Store por ejemplo.
  • TemporaryFolder: Aqui guardaremos información sin tener la necesidad de borrarla más tarde. La información se guardará entre las distintas sesiones pero cuando el sistema requiera espacio (Ejemplo: poca memoria disponible), eliminará la información.  Es un lugar idóneo donde guardar datos obtenidos de peticiones web, servicios o imágenes por ejemplo.

Roaming Data

Si un usuario obtiene la aplicación en múltiples dispositivos, Windows y Windows Phone, es fantástico para el que la configuración de la misma se comparta. De modo que, cualquier cambio realizado en la configuración de la Aplicación uno de los dispositivos se vea reflejado en el otro. Esto lo podemos conseguir utilizando Roaming Data entre aplicaciones Windows Phone 8.1 y Windows 8.1 que compartan el mismo PFN (Package Family Name).

El Roaming de datos nos proporciona una forma de compartir y sincronizar datos entre distintos dispositivos físicos de manera sencilla.

NOTA: Si publicamos dos versiones de la misma aplicación (una para la Windows Store y otra para la Windows Phone Store) podemos compartir configuración e información utilizando el mismo PFN en cada aplicación.

Roaming Data

Roaming Data

Utilizaremos diccionarios (clave / valor) que automáticamente se almacenarán en el OneDrive del usuario. El tamaño máximo de los datos Roaming viene establecido en la propiedad ApplicationData.RoamingStorageQuota (normalmente 100KB).

NOTA: Los datos de Roaming no afecta al tamaño de almacenamiento del usuario.

Manos a la obra

Comenzamos creando un nuevo proyecto:

Nueva Aplicación Universal

Nueva Aplicación Universal

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.

Nuestro objetivo en este ejemplo sera simular una opción de configuración sencilla, como por ejemplo, el color básico utilizado en la aplicación y utilizar datos Roaming para mantener el color seleccionado por el usuario sincronizado entre las aplicaciones Windows y Windows Phone.

Vamos a tener una configuración de colores por lo tanto empezaremos definiendo… el color. En nuestra carpeta Models crearemos la siguiente clase:

public class Accent
{
     public string Name { get; set; }

     public string Hex { get; set; }
}

Nos permite definir el nombre y valor hexadecimal de cada color. A continuación, en nuestra viewmodel tendremos una colección de colores:

private Accent _current;
private ObservableCollection<Accent> _accents;

public ObservableCollection<Accent> Accents
{
     get { return _accents; }
     set { _accents = value; }
}

public Accent Current
{
     get { return _current; }
     set
     {
          _current = value;
          RaisePropertyChanged("Current");
     }
}

Al entrar en la vista, utilizaremos un método para obtener el listado de colores disponibles:

_accents = new ObservableCollection<Accent>
{
     new Accent { Name = "Blue", Hex= "#1BA1E2" },
     new Accent { Name = "Brown", Hex= "#A05000" },
     new Accent { Name = "Green", Hex= "#339933" },
     new Accent { Name = "Pink", Hex= "#E671B8" },
     new Accent { Name = "Purple", Hex= "#A200FF" },
     new Accent { Name = "Red", Hex= "#E51400" },
     new Accent { Name = "Teal", Hex= "#00ABA9" },
     new Accent { Name = "Lime", Hex= "#A2C139 " },
     new Accent { Name = "Magenta", Hex= "#D80073 " }
};

Todo preparado para poder definir la interfaz:

<ListView
     Grid.Row="1"
     ItemsSource="{Binding Accents}"
     SelectedItem="{Binding Current, Mode=TwoWay}">
     <ListView.ItemsPanel>
         <ItemsPanelTemplate>
             <WrapGrid MaximumRowsOrColumns="3" />
         </ItemsPanelTemplate>
     </ListView.ItemsPanel>
     <ListView.ItemTemplate>
         <DataTemplate>
             <Grid Background="{Binding Hex}"
                   Height="100"
                   Width="100"
                   Margin="5">
                 <TextBlock Text="{Binding Name}"
                            VerticalAlignment="Bottom"/>
             </Grid>
         </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

Hasta aqui nuestro ejemplo básico. Un listado de colores que el usuario seleccionará àra utilizar como color principal en la aplicación. Utilizamos datos Roaming para guardar el color seleccionado. Para ello, comenzamos…

Asociando la aplicación con la Store

Para que la infraestructura de datos Roaming funcione (datos en OneDrive), debemos asociar la aplicación con la tienda.

NOTA: No es necesario publicar la aplicación para realizar pruebas.

Para asociar la aplicación con la tienda haremos clic derecho sobre la misma y elegiremos la opción «Associate App with the Store…»:

Reservar un nombre de App

Reservar un nombre de App

Nos aparecerá una ventana modal como la siguiente:

Asociar la App con la Store

Asociar la App con la Store

Registraremos un nombre de Aplicación:

Nombre de la App

Nombre de la App

Al finalizar el proceso, nuestra aplicación quedará asociada a la tienda y tendremos un resumen de todos los valores correctos a utilizar en el archivo de manifiesto:

Resumen

Resumen

Una vez realizado el proceso en la aplicación Windows Store o Windows Phone, debemos repetir lo mismo en la otra seleccionando el nombre reservado previamente. Antes de continuar sería oportuno revisar el archivo de manifiesto de cada aplicación para asegurar que el PFN (Package Family Name) es el mismo.

Misma cuenta Microsoft en Windows/Phone

Para que la sincronización de datos funcione correctamente, ambos dispositivos deben utilizar la misma cuenta Microsoft. Esto que es sencillo, en el punto final es fácil de cumplir pero quizas no tanto en el desarrollo. En la máquina de desarrollo tendremos una cuenta Microsoft establecida pero probablemente no en el emulador de Windows Phone. Recuerda utilizar la misma cuenta en el emulador para garantizar que todo funcione o utiliza un dispositivo físico.

Usando Roaming Settings

Llegamos a la parte central del artículo, el uso de Roaming. Para utilizar Roaming en nuestro ejemplo vamos a crear un servicio llamado RoamingSettingsService con la siguiente definición:

/// <summary>
/// Almacenar y recuperar configuraciones y archivos desde el almacén de datos móviles de aplicaciones.
/// </summary>
public interface IRoamingSettingsService
{
     void SaveData(string key, string value);

     void SaveData(string container, string key, string value);

     void SaveData(string key, ApplicationDataCompositeValue value);

     bool ContainsKey(string key);

     object GetData(string key);

     object GetData(string container, string key);

     ApplicationDataCompositeValue GetDataComposite(string key);

     void RemoveData(string key);

     ApplicationDataContainer CreateContainer(string container);

     void RemoveContainer(string container);
}

Ahora nos centramos en la definición del servicio. Comenzamos creando una propiedad RoamingSettings que accederá a la propiedad ApplicationData.RoamingSettings para obtener la configuración.

/// <summary>
///  Almacenar y recuperar configuraciones y archivos desde el almacén de datos móviles de aplicaciones.
///
/// Más info: http://msdn.microsoft.com/es-es/library/windows/apps/xaml/hh700362.aspx
/// </summary>
public class RoamingSettingsService : IRoamingSettingsService
{
     internal ApplicationDataContainer RoamingSettings
     {
         get { return ApplicationData.Current.RoamingSettings; }
     }
}

Definimos el método que nos permitirá guardar datos Roaming. Ya comentamos previamente que trabajaremos con diccionarios con clave y valor. El método para guardar datos recibirá la clave y el valor a almacenar:

/// <summary>
/// Escribir datos en una configuración.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void SaveData(string key, string value)
{
     RoamingSettings.Values[key] = value;
}

NOTA: Podríamos pasar un tercer parámetro para indicar la prioridad utilizando la propiedad HighPriority pero solo tendría efecto en la aplicación Windows Store.

Antes de almacenar un valor nos puede interesar verficar si ya existe un valor almacenado con la clave otorgada. Definiremos un sencillo método que nos haga esta verificación:

/// <summary>
/// Verifica si existe algun dato relacionado con la clave facilitada.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool ContainsKey(string key)
{
     return RoamingSettings.Values.ContainsKey(key);
}

Una vez guardado datos desearemos recuperarlos posteriormente… creamos el método que nos permite recuperar el valor de una clave otorgada:

/// <summary>
/// Leer datos desde una configuración.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object GetData(string key)
{
     return RoamingSettings.Values[key];
}

Utilizamos la propiedad ApplicationDataContainer.Values para acceder al valor almacenado con la clave dada.

Para eliminar una configuración, utilizaremos el método ApplicationDataContainerSettings.Remove:

/// <summary>
/// Eliminar configuraciones.
/// </summary>
/// <param name="key"></param>
public void RemoveData(string key)
{
     RoamingSettings.Values.Remove(key);
}

Utilizando el servicio en nuestra viewmodel, al cargar el color seleccionado verificaremos si esta ya guardado como una configuración almacenada en Roaming y en caso afirmativo, recuperaremos el valor. En caso de no existir, seleccionamos el primer color del listado:

if (_roamingSettingsService.ContainsKey(Key))
{
     var hex = _roamingSettingsService.GetData(Key).ToString();
     Current = Accents.First(c => c.Hex.Equals(hex));
}
else
     Current = Accents.First();

Cada vez que el usuario cambie de color, guardaremos el mismo:

public Accent Current
{
     get { return _current; }
     set
     {
          _current = value;
          _roamingSettingsService.SaveData(Key, _current.Hex);

          RaisePropertyChanged("Current");
     }
}
Configuración de color de la App

Configuración de color de la App

Recibiendo la notificación de cambio en los datos

El evento DataChanged se lanzará cada vez que los datos Roaming cambien siempre y cuando la aplicación este activa en el momento del cambio.

Debemos suscribirnos al evento:

ApplicationData.Current.DataChanged -=
                new TypedEventHandler<ApplicationData, object>(DataChangeHandler);

Hemos establecido DataChangeHandler como el controlador para cambios de datos móviles:

void DataChangeHandler(ApplicationData appData, object args)
{

}

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

Más opciones utilizando Roaming Data

Hasta este punto hemos visto todo lo necesario para utilizar datos en Roaming de manera simple, tal y como hemos usado en nuestro ejemplo. Sin embargo, tenemos muchas otras opciones disponibles. Si recordáis la definición de nuestro servicio veréis muchos otros métodos que no hemos usado hasta ahora. Vamos a revisarlos uno a uno.

Utilizamos el método ApplicationDataContainer.CreateContainer para crear un contenedor de configuraciones:

/// <summary>
/// Crea o abre el contenedor de configuración especificado.
/// </summary>
/// <param name="container"></param>
/// <returns></returns>
public ApplicationDataContainer CreateContainer(string container)
{
     return RoamingSettings.CreateContainer(container, ApplicationDataCreateDisposition.Always);
}

Utilizamos el método ApplicationDataContainer.DeleteContainer para eliminar el contenedor de configuraciones:

/// <summary>
/// Elimina el contenedor de configuración especificado.
/// </summary>
/// <param name="container"></param>
public void RemoveContainer(string container)
{
     RoamingSettings.DeleteContainer(container);
} 

Guardamos el valor value para acceder a la configuración dada por el parámetro key del contenedor container:

/// <summary>
/// Escribir datos en una configuración.
/// </summary>
/// <param name="container"></param>
/// <param name="key"></param>
/// <param name="value"></param>
public void SaveData(string container, string key, string value)
{
     RoamingSettings.Containers[container].Values[key] = value;
}

Usamos la propiedad ApplicationDataContainer.Values para acceder a la configuración dada por el parámetro key del contenedor container:

/// <summary>
/// Leer datos desde una configuración de un contenedor de configuración especificado.
/// </summary>
/// <param name="container"></param>
/// <param name="key"></param>
/// <returns></returns>
public object GetData(string container, string key)
{
     return RoamingSettings.Containers[container].Values[key];
}

Podemos guardar settings utilizando un objeto ApplicationDataCompositeValue que contiene un conjunto de  configuraciones:

/// <summary>
/// Escribir datos en una configuración.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void SaveData(string key, ApplicationDataCompositeValue value)
{
     RoamingSettings.Values[key] = value;
}

Consejos al usar Roaming Data

A la hora de utilizar Roaming Data es aconsejable que tengas en cuenta:

  • La sincronización de los datos se realiza en background.
  • Utilizar datos en Roaming es ideal para poca cantidad de información como por ejemplo la configuración de la aplicación, o pequeños volúmenes de datos relacionados con actividad reciente. Sin embargo, NO se debe utilizar Roaming para grandes cantidades de información.
  • La propiedad HighPriority para forzar una sincronización rápida solo esta disponible en Windows, no tiene efecto en Windows Phone.
  • A la hora de depurar aplicaciones que hagan uso de Roaming, si hay problemas en la sincronización, asegúrate que ambos dispositivos usan la misma cuenta Microsoft, que en el archivo de manifiesto tenemos el mismo PFN y la misma versión.

Más información

[Universal App] Usando las APIs Credential Locker

Login-01Introducción

En determinadas ocasiones, nuestras aplicaciones requieren seguridad o personalización por usuario. En estos casos son típicos problemas como, como almacenar usuario y contraseña, encriptar la información, gestionar múltiples dispositivos, etc.

Era un proceso «habitual» pero que requiere tener en cuentas bastantes aspectos. Ahora con la llegada de las APIs Credential Locker disponibles en Windows.Security.Credentials, todo el proceso es mucho más simple.

En este artículo vamos a conocer las APIs de Credential Locker utilizñandolas en un ejemplo real donde veremos como almacenar, recuperar y eliminar claves.

¿Te apuntas?

¿Credential Locker?

El almacén de credenciales nos permite almacenar y administrar de forma segura contraseñas de usuarios de una aplicación o servicio específico de modo que por un lado, los datos almacenados de una aplicación se transfieren automáticamente a los otros dispositivos de confianza del usuario, simplificando el proceso de autenticación tanto a los usuarios como a nosotros, y por otro lado, no es posible desde una aplicación o servicio acceder a los credenciales asociados con otra aplicación o servicio.

Primeros pasos

Comenzamos creando un nuevo proyecto:

Nueva Aplicación Universal

Nueva Aplicación Universal

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.

Nuestro objetivo sera muy sencillo. Nuestra aplicación de ejemplo pedirá fuente, usuario y contraseña permitiéndo con tres sencillos botones, añadir, recuperar y eliminar credenciales utilizando la API Credential Locker.

Comenzamos definiendo la interfaz de usuario:

<StackPanel Margin="12">
     <StackPanel Orientation="Vertical">
          <TextBox Header="Source" PlaceholderText="https://facebook.com" />
          <TextBox Header="User" PlaceholderText="user@mail.com" />
          <PasswordBox Header="Password" PlaceholderText="1234abcd" />
     </StackPanel>
     <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
          <Button Content="Guardar" />
          <Button Content="Recuperar" Margin="12, 0" />
          <Button Content="Borrar" />
     </StackPanel>
</StackPanel>

Muy simple. Permitimos obtener la información de seguridad requerida y contamos con tres botones de acción. En la viewmodel correspondiente contaremos con propiedades para obtener la información escrita en cada una de las cajas de texto:

// Variables
private string _source;
private string _user;
private string _password;
private string _info;

public string Source
{
     get { return _source; }
     set { _source = value; }
}

public string User
{
     get { return _user; }
     set { _user = value; }
}

public string Password
{
     get { return _password; }
     set { _password = value; }
}

public string Info
{
     get { return _info; }
     set
     {
          _info = value;
          RaisePropertyChanged("Info");
     }
}

Y cada botón, ejecutará un comando en la viewmodel:

// Commands
private ICommand _saveCommand;
private ICommand _readCommand;
private ICommand _deleteCommand;

public ICommand SaveCommand
{
     get { return _saveCommand = _saveCommand ?? new DelegateCommand(SaveCommandDelegate); }
}

public ICommand ReadCommand
{
     get { return _readCommand = _readCommand ?? new DelegateCommand(ReadCommandDelegate); }
}

public ICommand DeleteCommand
{
     get { return _deleteCommand = _deleteCommand ?? new DelegateCommand(DeleteCommandDelegate); }
}

public void SaveCommandDelegate()
{

}

public void ReadCommandDelegate()
{

}

public void DeleteCommandDelegate()
{

}

De modo que nuestra interfaz bindeada a las propiedades y comandos correspondientes quedara como podemos ver a continuación:

<StackPanel Margin="12">
     <StackPanel Orientation="Vertical">
          <TextBox Text="{Binding Source, Mode=TwoWay}" Header="Source" PlaceholderText="https://facebook.com" />
          <TextBox Text="{Binding User, Mode=TwoWay}" Header="User" PlaceholderText="user@mail.com" />
          <PasswordBox Password="{Binding Password, Mode=TwoWay}" Header="Password" PlaceholderText="1234abcd" />
     </StackPanel>
     <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
          <Button Content="Guardar" Command="{Binding SaveCommand}" />
          <Button Content="Recuperar" Margin="12, 0" Command="{Binding ReadCommand}" />
          <Button Content="Borrar" Command="{Binding DeleteCommand}" />
     </StackPanel>
     <ScrollViewer>
          <TextBlock Text="{Binding Info}"/>
     </ScrollViewer>
</StackPanel>

Hasta aqui, la interfaz y estructura básica de nuestro ejemplo.

Interfaz del ejemplo

Interfaz del ejemplo

Continuamos centrándonos en la parte importante, la gestión de credenciales.

Gestión de credenciales

Para realizar la gestión de credenciales utilizaremos las APIs Credential Locker disponibles en Windows.Security.Credentials. Vamos a crear un servicio con la siguiente definición:

public interface IPasswordVaultService
{
     void Save(string resource, string userName, string password);

     PasswordCredential Read(string resource, string userName);

     IReadOnlyList<PasswordCredential> GetAll();

     void Delete(string resource, string userName);
}

NOTA: El servicio será inyectado por Ioc en nuestra viewmodel.

El servicio contará con cuatro métodos:

  • Save: Nos permitirá guardar credenciales.
  • Read: Nos permitirá recuperar un credencial concreto.
  • GetAll: Recupera todos los credenciales que tengamos almacenados en el Credential Locker.
  • Delete: Eliminará un credencial concreto almacenado previamente.

Nos centramos a continuación en la implementación de cada método. Comenzamos por el método Save:

public void Save(string resource, string userName, string password)
{
     PasswordVault vault = new PasswordVault();
     PasswordCredential cred = new PasswordCredential(resource, userName, password);
     vault.Add(cred);
}

Analicemos el código superior. Primero, obtenemos una referencia al Credential Locker usando on objeto de tipo PasswordVault definido en el namespace Windows.Security.Credentials. Continuamos creando un objeto de tipo PasswordCredential que representará el credencial a almacenar con la referencia a nuestra Aplicación o el tipo de Login utilizado además de los credenciales en si, usuario y contraseña. Añadiremos el credencial creado al almacén de credenciales utilizando el método PasswordVault.Add.

Continuamos con el método Read:

public PasswordCredential Read(string resource, string userName)
{
     PasswordVault vault = new PasswordVault();

     return vault.Retrieve(resource, userName);
}

Contamos con una gran variedad de opciones para recuperar credenciales del almacén. En el código de la parte superior utilizamos la forma más simple posible. Contando con el nombre de la App o tipo de Login además del nombre de usuario, podemos recuperar la información utilizando el método PasswordVault.Retrieve.

En el método GetAll utilizamos el método PasswordVault.RetrieveAll  para recuperar todos los credenciales almacenados en la Aplicación:

public IReadOnlyList<PasswordCredential> GetAll()
{
     PasswordVault vault = new PasswordVault();

     return vault.RetrieveAll();
}

Además de las dos formas utilizadas contamos con otras opciones para recuperar credenciales:

Por último, nos centramos en el método Delete, que como podemos imaginar se encargará de eliminar un credencial en concreto:

public void Delete(string resource, string userName)
{
     PasswordVault vault = new PasswordVault();
     PasswordCredential cred = vault.Retrieve(resource, userName);
     vault.Remove(cred);
}

De nuevo, es un proceso muy sencillo que podemos hacer con pocas líneas. Accedemos de nuevo al almacén de credenciales mediante un objeto de tipo PasswordVault y utilizamos el método PasswordVault.Remove  para eliminar el credencial almacenado pasado como parámetro.

Con nuestro servicio para gestionar el almacén de credenciales preparado, solo nos falta definir la acción de cada comando. Al guardar el credencial:

public void SaveCommandDelegate()
{
     if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(User) || string.IsNullOrEmpty(Password))
     {
          return;
     }

     try
     {
          _passwordVaultService.Save(Source, User, Password);
          Info += string.Format("Datos guardatos. Resource: {0}, User: {1}, Password: {2}",
          Source, User, Password)
     }
     catch(Exception ex)
     {
          Info += ex.Message;
     }
}
Guardando credenciales

Guardando credenciales

Sencillo, verificamos que los datos que definen el credencial son válidos y en caso afirmativo utilizamos el método Save de nuestro servicio PasswordVaultService. Para recuperar un credencial:

public void ReadCommandDelegate()
{
     if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(User))
     {
          Info += "La fuente y el usuario son obligatorios.";
          return;
     }

     try
     {
          var cred = _passwordVaultService.Read(Source, User);
          Info += string.Format("Datos obtenidos con éxito. Resource: {0}, User: {1}, Password: {2}",
          cred.Resource, cred.UserName, cred.Password);
     }
     catch (Exception ex)
     {
          Info += ex.Message;
     }
}
Recuperar un credencial

Recuperar un credencial

Utilizaremos tras validar la información, el método Read de nuestro servicio. Y por último, en el comando para eliminar credenciales:

public void DeleteCommandDelegate()
{
     if (string.IsNullOrEmpty(Source) || string.IsNullOrEmpty(User))
     {
          Info += "La fuente y el usuario son obligatorios.";
           return;
     }

     try
     {
          _passwordVaultService.Delete(Source, User);
          Info += string.Format("Datos eliminados con éxito. Resource: {0}, User: {1}, Password: {2}",
          Source, User, Password);
     }
     catch (Exception ex)
     {
          Info += ex.Message;
     }
}

Utilizamos el método Delete del servicio.

Eliminar un credencial

Eliminar un credencial

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

Buenas prácticas

  • El almacén de credenciales esta pensado para facilitar la tarea de la gestión de la seguridad en nuestras aplicaciones. No se recomienda su uso para almacenar grandes cantidades de información. Contamos con otras APIs válidas y más adecuadas para esta necesidad.
  • Debemos controlar el ciclo de almacenamiento de credenciales en el Password Vault correctamente. No almacenar información hasta haber sido autenticado correctamente y haber marcado el usuario que desea recordar la información.

Conclusiones

En aplicaciones universales, es decir, tanto en aplicaciones Windows Phone como en aplicaciones Windows Store, contamos con una API, disponible bajo el namespace Windows.Security.Credentials llamada Credential Locker que nos permite gestionar credenciales de usuario con suma facilidad. La gran ventaja de utilizar la API es que nos almacena la información en un almacén seguro, la información es encriptada al ser almacenada. Además, otra de las grandes ventajas de utilizar la API es el roaming de los credenciales entre dispositivos de confianza bajo la misma cuenta Microsoft.

Más información