[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

[Tips and Tricks] Utilizar la extensión Behaviors SDK en una App Universal Windows 10

Magic-WandIntroducción

Con la llegada del SDK de Windows 10 Preview tenemos la posibilidad de crear Apps Universales con un único binario que funcione en múltiples plataformas. En ocasiones desearemos utilizar caracteristicas específicas de una plataforma. Para ello, utilizaremos el SDK de extensiones. Haremos clic derecho sobre las referencias del proyecto, Añadir referencia y accedemos al apartado Extensiones dentro del apartado Universal App Platform:

Extensions SDK

Extensions SDK

Anteriormente, en extensiones, teníamos el SDK de Behaviors, ¿dónde esta ahora?.

Behaviors… ¿dónde?, ¿cómo?

Los Behaviors (o comportamientos en Español) nos permiten añadir lógica directamente en XAML para realizar acciones sin necesidad de escribir código extra en el code behind.

En proyectos Windows y Windows Phone 8.1, al añadir referencias, teníamos la extensión Behaviors SDK (XAML) dentro del apartado extensiones de Windows 8.1:

Behaviors SDK

Behaviors SDK

El “truco”

Para poder utilizar la extensión Behaviors SDK en proyectos UAP, copiamos los ficheros de la ruta:

C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1\ExtensionSDKs

A la ruta:

C:\Program Files (x86)\Microsoft SDKs\UAP\v0.8.0.0\ExtensionSDKs
Copiamos Behaviors SDK

Copiamos Behaviors SDK

Tras reiniciar Visual Studio 2015 y acceder al apartado de extensiones:

Behaviors SDK disponible!

Behaviors SDK disponible!

Voila!

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

[Xamarin.Forms] Novedades de Xamarin.Forms 1.3: Behaviors

xamarinhqNovedades de Xamarin.Forms 1.3

Recientemente Xamarin lanzo la version 1.3 definitiva de Xamarin.Forms con una cantidad de novedades muy sustansiosa. Entre otras novedades contamos con:

  • Soporte para Estilos.
  • Soporte para Behaviors.
  • Soporte para Triggers (DatTriggers y MultiTriggers).
  • Nuevo miembro en las páginas, OnBackButtonPressed para gestionar la pulsación del botón físico atrás.
  • Mejoras en el DependencyService para añadir más flexibilidad a la hora de registrar dependencias.
  • Corrección de bugs.

En este artículo vamos a analizar todas las novedades relacionadas con el uso de behaviors en Xamarin.Forms 1.3.

¿Te apuntas?

Behaviors

El concepto es algo muy sencillo. Un Behavior espera por “algo” para hacer “algo”. Concretamos más. Un Behavior espera por “algo”. Puede ser un evento que se lanza, el cambio de una propiedad o cualquier otra acción personalizada que deseamos monitorear. Una vez que ese “algo” se desencadena, el Behavior puede hacer acciones muy variadas, desde cambiar el valor de una propiedad, lanzar un evento, hacer verificaciones o validaciones, etc.

Los Behaviors nos permiten encapsular lógica que se puede adjuntar a un componente específico. Generalmente ayudan a personalizar o completar ciertos componentes e incluso en muchas ocasiones son un a ayuda fundamental para mantener una estructura idónea al implementar patrones como MVVM.

Comenzamos creando un nuevo proyecto desde cero:

Nuevo proyecto Xamarin Forms

Nuevo proyecto Xamarin Forms

Tras crear el proyecto procedemos a actualizar a Xamarin.Forms 1.3. Para ello, vamos a gestionar los paquetes NuGet a nivel de solución.

NuGet Xamarin.Forms 1.3

NuGet Xamarin.Forms 1.3

NOTA: Si tenéis problemas al instalar Xamarin. 1.3 aseguráos de contar con la última versión de NuGet. Versiones antiguas de NuGet pueden ocasionar problemas en la instalación. Para asegurar tener la última versión debemos dirigirnos a Herramientas > Extensiones y Actualizaciones -> Instalados. Aqui podremos seleccionar NuGet e instalar la última versión.

El objetivo que buscaremos en este ejemplo sera crear un Behavior analizando todas las partes implicadas que permitira solo introducir valores numéricos en una caja de texto.

Comenzamos creando el behavior. Crearemos una clase que heredará de Behavior<T>:

public class NumericEntryBehavior : Behavior<Entry>
{

}

Pasamos a ver la implementación:

public class NumericEntryBehavior : Behavior<Entry>
{
     private string _lastValidText;

     protected override void OnAttachedTo(Entry bindable)
     {
         bindable.TextChanged += EntryTextChanged;
         base.OnAttachedTo(bindable);
     }

     protected override void OnDetachingFrom(Entry bindable)
     {
         bindable.TextChanged -= EntryTextChanged;
         base.OnDetachingFrom(bindable);
     }

     private void EntryTextChanged(object sender, EventArgs e)
     {
         var entry = sender as Entry;
         if (entry != null)
         {
             double value;
             if (string.IsNullOrEmpty(entry.Text) ||
                 Double.TryParse(entry.Text, out value))
             {
                 _lastValidText = entry.Text;
                 return;
             }

             entry.Text = _lastValidText;
         }    
     }
}

Cada vez que el usuario escriba algo en la caja de texto haremos una verificación para determinar si se trata de un valor numérico. Para ello, sobreescribimos el método OnAttachedTo donde recibiremos el objeto Entry y nos suscribiremos a su evento TextChanged. De igual forma, sobreescribimos el método OnDetachingFrom donde eliminaremos la suscripción al evento y el Behavior del objeto Entry. En el evento TextChanged intentamos parsear el contenido recibido a Double para verificar si es un dígito o no. De esa forma, vamos comprobando que lo que escribe el usuario son dígitos o no.

NOTA: Para aquellos que tengáis experiencia definiendo Behaviors en la plataforma Windows habréis observado algunas diferencias. No tenemos acceso al objeto AssociatedObject. En cambio, el objeto asociado se inyecta en los casos necesarios.

Una vez definido el Behavior es hora de implementarlo en nuestra interfaz de usuario. Para acceder al mismo debemos definir el namespace donde se encuentra:

xmlns:behaviors="clr-namespace:Entry_Behavior.Behaviors;assembly=Entry_Behavior"

Simple. Utilizamos la sintaxis clr-namespace para definir el namespace donde se encuentra definido.

<Entry>
     <Entry.Behaviors>
        <behaviors:NumericEntryBehavior />
     </Entry.Behaviors>
</Entry>

Vamos a analizar el trozo de XAML anterior. Cada elemento que hereda de VisualElement cuenta con una propiedad de tipo IList<Behavior>. Para utilizar nuestro behavior NumericEntryBehavior, dentro de la colección de Behaviors del control Entry, definimos el mismo utilizando el espacio de nombres behaviors, definido previamente.

NOTA: De nuevo, para aquellos con experiencia previa con Behaviors en la plataforma Windows. En Xamarin.Forms cada elemento heredado de VisualElement cuenta con una propiedad Behaviors. No es necesario importar librerías para poder trabajar con Behaviors.

Y todo listo!. Si ejecutamos nuestro ejemplo podemos probar el “comportamiento” añadido al control Entry. Al escribir información veremos que solo se permiten valores numéricos o carácteres como la coma en caso de desear escribir decimales.

El resultado es el siguiente:

Nuestro Behavior en funcionamiento!

Nuestro Behavior en funcionamiento!

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

Recordar que podéis dejar en los comentarios cualquier tipo de sugerencia o pregunta.

Más información