[Windows 10] Navegación en Apps Windows

Introducción

Casi cualquier aplicación que realices tendrá más de una página. Por lo que es vital saber como navegar entre ellas.

En este artículo, vamos a aprender como se realiza la navegación entre páginas en Apps Windows
primero desde un punto de vista teórica para terminar realizando un
ejemplo paso a paso cubriendo todos los aspectos relacionados con la
navegación utilizando el patrón MVVM.

¿Te apuntas?

Navegación entre páginas

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). Contamos con un modelo de navegación integrado que usa controles Frame y Page y funciona de manera similar a un navegador web. El control Frame hospeda controles Page
y tiene un historial de navegación que se puede utilizar para ir hacia
atrás y hacia adelante por las páginas que ya visitaste. Tras obtener el
Frame correspondiente, utilizamos el método Navigate para realizar la navegación a otra página. Tenemos dos sobrescrituras del método Navigate:

  • Navigate(TypeName). Provoca que el Frame cargue el contenido especificado por el tipo pasado como parámetro.
  • Navigate(TypeName, Object).
    En este caso, además de indicar el tipo del contenido a cargar (primer
    parámetro), podemos pasar un parámetro a la página que se navega(segundo
    parámetro).

Navegar atrás

Utilizamos primero la propiedad CanGoBack que
nos devuelve un bool indicando si hay páginas en el historial de
navegación o no. Si existe historial de navegación utilizamos el método GoBack que navega al elemento más inmediato del historial de navegación.

NOTA: También contamos con la propiedad CanGoForward. Indica si en el historial de navegación existe una página delante de la actual.

Pasar información entre páginas

Hemos visto la teoría necesaria para navegar hacia delante y hacia
atrás. Sin embargo, en ocasiones, necesitamos pasar información entre
las páginas. Ya hemos visto que el método Navigate cuenta con una sobrescritura que nos permite pasar parámetros.

Navigate(TypeName, Object).
En este caso, además de indicar el tipo del contenido a cargar (primer
parámetro), podemos pasar un parámetro a la página que se navega(segundo
parámetro).

Al navegar hacia o desde una página se ejecutan los métodos:

Para recuperar el objeto pasado como parámetro vamos a utilizar el método OnNavigatedTo que se ejecutará al entrar en la segunda página. El argumento de tipo NavigationEventArgs  cuenta con una propiedad Parameter.

Manos a la obra!

Para probar todas las posibilidades de navegación crearemos un nuevo proyecto UAP:

Nuestro objetivo en el ejemplo sera crear múltiples páginas y navegar entre ellas.

Realmente podemos mantener una implementación MVVM
muy similar a como hacíamos hasta ahora en Apps Universales para Windows
y Windows Phone 8.1 salvando las diferencias a nivel de proyectos (tres
VS uno). Comenzamos por la base. Contamos con dos ficheros clave en
nuestro proyecto para implementar el patrón MVVM:

  • PageBase
  • ViewModelBase

PageBase

Como revelamos con su nombre, será una clase de la que heredaran
todas las páginas de nuestra aplicación. Y si, comentamos que serán
todas las páginas de la aplicación (ya sean del proyecto Windows o del
proyecto Windows Phone). Las páginas en ambas plataformas es exactamente
igual, un objeto de tipo Page. Esta clase cuenta con varios objetivos:

  • Establecer el Frame activo en todo momento para que el acceso al mismo sea sencillo.
  • Permitir el acceso de los eventos de navegación desde nuestras viewmodels.

Además podría interesarnos además:

  • Gestionar las transiciones entre páginas.
  • Gestionar estados (Loading, etc.)
  • Gestionar el estado segun la conexión de red.

Con la funcionalidad básica (facilitarnos el acceso a Frame y eventos de navegación) quedaría:

public class PageBase : Page
{
        private ViewModelBase _vm;
 
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
 
            _vm = (ViewModelBase)this.DataContext;
            _vm.SetAppFrame(this.Frame);
            _vm.OnNavigatedTo(e);
        }
 
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            _vm.OnNavigatedFrom(e);
        }
}

Gestionamos los eventos básicos de navegación (al entrar y al salir de la página) de modo que, en el método OnNavigateTo obtenemos la viewmodel de la página y asignamos el Frame.

ViewModelBase

Continuamos con la segunda de nuestras clases base. En esta ocasión
trataremos la clase base de la que heredarán todos los viewmodels. Los
objetivos básicos de la clase son:

  • Notificar cambios (implementar INotifyPropertyChanged).
  • Acceso al objeto Frame que nos permitirá realizar la navegación.
  • Permitir el acceso a los eventos de navegación.
public abstract class ViewModelBase : INotifyPropertyChanged
{
        private Frame appFrame;
        private bool isBusy;
 
        public ViewModelBase()
        {
        }
 
        public Frame AppFrame
        {
            get { return appFrame; }
        }
 
        public bool IsBusy
        {
            get { return isBusy; }
            set
            {
                isBusy = value;
                RaisePropertyChanged();
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        public abstract Task OnNavigatedFrom(NavigationEventArgs args);
 
        public abstract Task OnNavigatedTo(NavigationEventArgs args);
 
        public void RaisePropertyChanged([CallerMemberName]string propertyName = "")
        {
            var Handler = PropertyChanged;
            if (Handler != null)
                Handler(this, new PropertyChangedEventArgs(propertyName));
        }
 
        internal void SetAppFrame(Frame viewFrame)
        {
            appFrame = viewFrame;
        }
}

El acceso a los eventos de navegación lo lograremos implementando en la viewmodel los métodos abstractos OnNavigatedFrom y OnNavigatedTo. Esto no permite guardar y recuperar parámetros o estados.

Utilizando PageBase

En la carpeta Views añadiremos nuestras páginas. En nuestro ejemplo, tendremos dos páginas:

  • FirstView
  • SecondView

Cada página será un objeto de tipo PageBase, tanto en XAML:

<base:PageBase
    x:Class="Navigation.Views.FirstView"
    xmlns:local="using:Navigation"
    xmlns:base="using:Navigation.Views.Base"
    mc:Ignorable="d"
    DataContext="{Binding FirstViewModel, Source={StaticResource Locator}}">
    <Grid>
    </Grid>
</base:PageBase>

Como en el code-behind:

public sealed partial class FirstView : PageBase
{
     public FirstView()
     {
         this.InitializeComponent();
     }
}

Añadimos en la primera vista (FirstView) dos botones que permita navegar a la segunda vista sin pasar y pasándo parámetro:

<Grid
     Background="LightBlue">
     <StackPanel
          HorizontalAlignment="Center"
      VerticalAlignment="Center">
      <Button
        Content="Go to Second View"
        Command="{Binding GoToSecondCommand}" />
      <Button
        Content="Pass parameter"
        Command="{Binding ParameterCommand}"
        Margin="0, 10"/>
     </StackPanel>
</Grid>

El resultado visual:

Para permitir navegar a la segunda vista, necesitamos definir dos
comandos (uno navegará sin pasar parámetro y otro pasándolo) en la
viewmodel. Creamos una clase derivada de ViewModelBase:

public class FirstViewModel : ViewModelBase
{
 
}

Implementamos los métodos de navegación definidos en ViewModelBase:

public class FirstViewModel : ViewModelBase
{
    public override Task OnNavigatedFrom(NavigationEventArgs args)
    {
        return null;
    }
 
    public override Task OnNavigatedTo(NavigationEventArgs args)
    {
        return null;
    }
}

Y añadimos comandos que permitan navegar de la página principal a la segunda página:

public class FirstViewModel : ViewModelBase
{
        private ICommand _goToSecondCommand;
        private ICommand _parameterCommand;
 
    public override Task OnNavigatedFrom(NavigationEventArgs args)
    {
        return null;
    }
 
    public override Task OnNavigatedTo(NavigationEventArgs args)
    {
        return null;
    }
 
    public ICommand GoToSecondCommand
    {
        get { return _goToSecondCommand = _goToSecondCommand ?? new DelegateCommand(GoToSecondCommandExecute); }
    }
 
    public ICommand ParameterCommand
    {
        get { return _parameterCommand = _parameterCommand ?? new DelegateCommand(ParameterCommandExecute); }
    }
 
    private void GoToSecondCommandExecute()
    {
        AppFrame.Navigate(typeof(SecondView));
    }
 
    private void ParameterCommandExecute()
    {
        var rnd = new Random();
        AppFrame.Navigate(typeof(SecondView), rnd.Next(1, 100));
    }
}

Recordamos que el control Frame hospeda controles Page
y tiene un historial de navegación que se puede utilizar para ir hacia
atrás y hacia adelante por las páginas que ya visitaste. Tras obtener el
Frame correspondiente, utilizamos el método Navigate para realizar la navegación a otra página. Tenemos dos sobrescrituras del método Navigate:

  • Navigate(TypeName). Provoca que el Frame cargue el contenido especificado por el tipo pasado como parámetro.
  • Navigate(TypeName, Object).
    En este caso, además de indicar el tipo del contenido a cargar (primer
    parámetro), podemos pasar un parámetro a la página que se navega(segundo
    parámetro).

En nuestro ejemplo hemos utilizado la segunda sobrescritura del método pasando un parámetro.

Ahora ,necesitamos conectar nuestra vista con nuestro viewmodel.
Podemos hacerlo de múltiples formas, desde el constructor de la vista,
instanciandola en App, usando Ioc. En nuestro caso, utilizaremos Ioc.
Usaremos Unity.

Una vez añadida la referencia correspondiente en cada proyecto (Windows y Windows Phone) creamos una nueva clase en la carpeta Base dentro de la carpeta ViewModels llamada ViewModelLocator:

public class ViewModelLocator
{
     readonly IUnityContainer _container;
 
     public ViewModelLocator()
     {
            _container = new UnityContainer();
 
            _container.RegisterType<FirstViewModel>();
            _container.RegisterType<SecondViewModel>();
     }
 
     public FirstViewModel FirstViewModel
     {
            get { return _container.Resolve<FirstViewModel>(); }
     }
 
     public SecondViewModel SecondViewModel
     {
            get { return _container.Resolve<SecondViewModel>(); }
     }
}

Sencillamente registramos nuestras viewmodels y creamos un par de
propiedades públicas por cada viewmodel para poder resolverlas y acceder
a ellas desde las vistas. A continuación, registramos nuestro locator en App.xaml:

<locator:ViewModelLocator x:Key="Locator" />

Asignamos la viewmodel como DataContext de nuestra viewmodel:

DataContext="{Binding FirstViewModel, Source={StaticResource Locator}}"

Y todo preparado!. Nuestra segunda página sera similar a la primera (tipo PageBase):

<base:PageBase
    x:Class="Navigation.Views.SecondView"
    xmlns:local="using:Navigation.Views"
    xmlns:base="using:Navigation.Views.Base"
    mc:Ignorable="d"
    DataContext="{Binding SecondViewModel, Source={StaticResource Locator}}">
    <Grid
        Background="LightGreen">
        <StackPanel
            HorizontalAlignment="Center"                    
            VerticalAlignment="Center">
            <Button
                Content="Go Back"
                Command="{Binding BackCommand}" />
            <TextBlock
                Text="{Binding Parameter}"
                Visibility="{Binding Parameter, Converter={StaticResource IntToVisibilityConverter}}"
                FontSize="48" />
        </StackPanel>
    </Grid>
</base:PageBase>

Contará con su propia viewmodel:

public class SecondViewModel : ViewModelBase
{
    // Variables
    private int _parameter;
 
    // Commands
    private ICommand _backCommand;
 
    public override Task OnNavigatedFrom(NavigationEventArgs args)
    {
        return null;
    }
 
    public override Task OnNavigatedTo(NavigationEventArgs args)
    {
            if (args.Parameter != null)
        {
            Parameter = Convert.ToInt32(args.Parameter);
                }
 
        return null;
    }
 
    public int Parameter
    {
        get { return _parameter; }
        set
        {
            _parameter = value;
            RaisePropertyChanged();
        }
    }
 
    public ICommand BackCommand
    {
        get { return _backCommand = _backCommand ?? new DelegateCommand(BackCommandExecute); }
    }
 
    private void BackCommandExecute()
    {
        if (AppFrame.CanGoBack)
            AppFrame.GoBack();
    }
}

Utilizamos el método GoBack del
Frame que navega al elemento más inmediato del historial de navegación,
la página anterior. Por otro lado, en caso de recibir parámetro, lo
asignamos a una propiedad bindeada en la vista. En nuestro ejemplo
pasamos un valor aleatorio entero entre 1 y 100.

En caso de utilizar el segundo botón, pasaremos el parámetro, se capturará en el método OnNavigatedTo y se bindeará en la vista:

Gestión de la tecla física volver

En nuestra aplicación, ejecutándola en Windows Phone y estando en la
segunda página, podemos utilizar también la tecla física como hacíamos
en Windows Phone 8 para volver atrás. Si la pulsamos… Ops!, ¿que ha ocurrido?

No hemos vuelto a la página anterior. En aplicaciones Windows XAML, al pulsar la tecla física atrás provoca una navegación a la App anterior no a la página anterior.

NOTA: En aplicaciones Windows Phone Silverlight 8.1 la navegación atrás es dentro de la propia aplicación.

Podemos incluir código personalizado para anular este comportamiento y
causar una navegación atrás dentro de la propia aplicación.

Tras añadir la extensión Mobile:

Windows Mobile Extension SDK

En App.xaml.cs:

if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
     HardwareButtons.BackPressed += HardwareButtons_BackPressed;

Utilizamos el método IsTypePresent de la clase estática ApiInformation incluida dentro del namespace Windows.Foundation.Metadata.

void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
     Frame frame = Window.Current.Content as Frame;
  
     if (frame == null)
          return;
  
     if (frame.CanGoBack)
     {
          frame.GoBack();
          e.Handled = true;
     }
}

Analizamos que hemos hecho. Cada vez que se pulse la tecla física
volver, entramos en nuestro código personalizado. Accedemos al Frame de
la aplicación, y si no es nulo, navegamos atrás y anulamos el
comportamiento por defecto.

Podéis descargar el 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] Universal Apps. Behavior para gestionar gestos swipe con comando

Introducció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

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

Introducció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:

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:

El “truco”

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

C:Program Files (x86)Microsoft SDKsWindowsv8.1ExtensionSDKs

A la ruta:

C:Program Files (x86)Microsoft SDKsUAPv0.8.0.0ExtensionSDKs

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

Voila!

Más información

[Windows 10] Adaptive Triggers

Introducción

En la plataforma Windows contámos con una gran variedad de
dispositivos, PCs, tabletas, Phablets, teléfonos. Contamos con una gran
cantidad diversa de tamaños de pantalla, resoluciones y otros factores
que como desarrolladores debemos tener en cuenta. El usuario es
exigente, debe serlo. Es vital poder disfrutar de la mejor experiencia posible en cada uno de los dispositivos, y ese, es uno de nuestros retos.

Adaptando la interfaz

Adaptar la interfaz de usuario a distintos tamaños, orientaciones y
otros cambios no es algo nuevo. Con la llegada de las Apps Modern UI en
Windows 8.0 debíamos gestionar la vista Narrow. Con las Apps Universales
en Windows 8.1 desarrollábamos Apps con Windows Phone y Windows en
mente. Ante este tipo de situaciones, hasta ahora hemos utilizado
diferentes técnicas. Las más habituales son:

  • Vistas XAML separadas por plataforma.
  • Diseño flexible. Utilizar posiciones y tamaños relativos para permitir escalar facilmente.
  • Utilizar diferentes estados visuales (Visual States) para gestionar vistas en diferentes dispositivos, pantallas e incluso orientaciones.

Adaptive Triggers

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. Es un paso importante pero que
conlleva realizar una acción que sera comun, diferenciar entre las
diferentes plataformas donde correrá dicho binario para poder adaptar la
interfaz de usuario. Con ese objetivo llegan los Adaptive Triggers. Recibimos potentes novedades en los Visual States:

  • Podemos modificar propiedades sin necesidad de animaciones.
    Anteriormente, la síntaxis era mucho más verbosa y necesitábamos
    utilizar StoryBoards para cambiar cualquier propiedad por simple que
    fuese.
  • Contamos con los StateTrigger. Podemos lanzar Triggers
    cuando se aplica un estado visual sin necesidad de código adyacente en
    el code-behind. El concepto es muy similar a los Triggers que tenemos
    disponible en WPF y Silverlight y el objetivo es el mismo, realizar un
    cambio cuando una condición cambia.

Actualmente contamos con un tipo de StateTrigger por defecto, el Adaptive Trigger que cuenta con los siguientes tipos:

  • MinWindowWidth
  • MinWindowHeight

Ambos nos permiten detectar cambios dinámicos en el tamaño
de la pantalla de modo que se adapte el tamaño de la pantalla entre
pantallas pequeñas y grandes. El funcionamiento es similar a las media
queries en CSS por ejemplo. Se crearán diferentes estados estableciendo
unos altos y anchos mínimos, de modo que, cuando la pantalla es más
grande que el tamaño asignado se activará el estado visual.

NOTA: Los valores se especifican en pixeles.

Para probar las posibilidades de los Adaptive Triggers crearemos un nuevo proyecto UAP:

Nuestro objetivo en el ejemplo serán:

  • Aprender a utilizar AdaptiveTrigger de la forma más sencilla
    posible. Utilizaremos un texto que modificaremos segun ciertas
    condiciones del ancho de la ventana.
  • Crearemos un StateTrigger personalizado.

Añadimos en nuestra infertaz un control para mostrar texto:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
     <TextBlock x:Name="Info"
                HorizontalAlignment="Center"
        VerticalAlignment="Center" />
</Grid>

Sera el control que modificaremos mediante el VisualState:

<!-- Using AdaptiveTrigger -->
<VisualState x:Name="Small">
     <VisualState.StateTriggers>
          <AdaptiveTrigger MinWindowWidth="0" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Pantalla pequeña" />
          <Setter Target="Info.FontSize" Value="32" />
          <Setter Target="Info.Foreground" Value="Red" />
     </VisualState.Setters>
</VisualState>
<VisualState x:Name="Big">
     <VisualState.StateTriggers>
          <AdaptiveTrigger MinWindowWidth="600" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Pantalla grande" />
          <Setter Target="Info.FontSize" Value="48" />
          <Setter Target="Info.Foreground" Value="Blue" />
     </VisualState.Setters>
</VisualState>

Analicemos el trozo de XAML de la parte superior. Hemos definido dos estados visuales donde utilizamos la propiedad MinWindowWidth del StateTrigger AdaptiveTrigger
para aplicar una serie de cambios en las propiedades del control
TextBlock Info. Podemos encadenar tantos Setters como sean necesarios y
no es necesario la gestión por medio de StoryBoards lo que nos permite
definirlo todo de una manera menos verbosa.

Si lanzamos nuestra App y el tamaño de la ventana es superior a 600 px veremos algo como lo siguiente:

Mientras que si reducimos el tamaño:

Muy potente, ¿verdad?. De esta forma podemos hacer distintos cambios
en la organización de elementos, estilos y otros detalles para ajustar
la interfaz de usuario segun el tamaño de la ventana.

Creando Adaptive Triggers personalizados

Hasta ahora hemos aprendido que:

  • Tenemos potentes novedades en los VisualStates.
  • Como gestionar los elementos de la interfaz de usuario para adaptar la interfaz de usuario a cada dispotivo.
  • El uso de Adaptative Triggers para controlar el ancho y/o alto de la pantalla y adaptar la UI.

Los Triggers disponibles son lo suficientemente potentes como para
permitirnos un trabajo correcto adaptando la interfaz de usuario pero…
¿podemos llegar más lejos?, ¿y si no es suficiente?

Si analizamos la clase AdaptiveTrigger utilizada hasta ahora:

Vemos que hereda de una clase base llamada StateTriggerBase disponible dentro del namespace Windows.UI.Xaml.

Podemos crear nuestros propios StateTriggers.

Crearíamos nuevas clases heredando de la clase mencionada. Vamos a
crear un nuevo StateTrigger que nos indique en que plataforma estamos
(Windows o Windows Phone).

Creamos una clase que herede de StateTriggerBase:

public class PlatformStateTrigger : StateTriggerBase
{
 
}

Al igual que en AdaptiveTrigger contamos con dos propiedades, MinWindowWidth y MinWindowHeight que nos permiten lanzar el Trigger dependiendo de condiciones utilizando las mismas.

En nuestro StateTrigger crearemos una propiedad de dependencia llamada Platform que nos permitirá pasar el nombre de la plataforma, de modo que si estamos en dicha plataforma lanzar la condición:

public string Platform
{
     get { return (string)GetValue(PlatformProperty); }
     set { SetValue(PlatformProperty, value); }
}
 
public static readonly DependencyProperty PlatformProperty =
     DependencyProperty.Register("Platform", typeof(string), typeof(PlatformStateTrigger),
     new PropertyMetadata(true, OnPlatformPropertyChanged));
 
private static void OnPlatformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
 
}

La parte más importante del Trigger es determinar con exactitud la
plataforma en la que se ejecuta la App. Entre las opciones disponibles,
la más sencilla es utilizar ResourceContext:

var qualifiers = ResourceContext.GetForCurrentView().QualifierValues;
_deviceFamily = qualifiers.First(q => q.Key.Equals("DeviceFamily")).Value;

Accedemos al listado de QualifierValues, un IObservableMap con claves
y valor que contiene información como el idioma de la App, el
contraste, el tema utilizado o la familia del dispositivo. Utilizaremos
esta última para determinar si estamos en Desktop o Phone.

De modo que ante el cambio de nuestra propiedad:

private static void OnPlatformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
     var obj = (PlatformStateTrigger)d;
     var platform = (string)e.NewValue;
 
     if(_deviceFamily.Equals("Desktop"))
    obj.SetTriggerValue(platform == "Windows");
     else
    obj.SetTriggerValue(platform == "WindowsPhone");
}

Nuestro objetivo final es llamar al método SetTriggerValue(bool)
que es el encargado de determinar si la condicion del estado se cumple o
no. Dependiendo del dispositivo lanzamos la condición correcta.

Por último, queda utilizar nuestro StateTrigger personalizado. En nuestra interfaz declaramos antes que nada el namespace donde lo hemos definido:

xmlns:customStateTriggers="using:AdaptiveTriggers.CustomStateTriggers"

Podemos crear VisualStates utilizando PlatformStateTrigger:

<VisualState x:Name="WindowsPhone">
     <VisualState.StateTriggers>
          <customStateTriggers:PlatformStateTrigger Platform="WindowsPhone" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Windows Phone" />
          <Setter Target="Info.FontSize" Value="32" />
          <Setter Target="Info.Foreground" Value="Red" />
     </VisualState.Setters>
</VisualState>
<VisualState x:Name="Windows">
     <VisualState.StateTriggers>
          <customStateTriggers:PlatformStateTrigger Platform="Windows" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
          <Setter Target="Info.Text" Value="Windows" />
          <Setter Target="Info.FontSize" Value="48" />
          <Setter Target="Info.Foreground" Value="Blue" />
     </VisualState.Setters>
</VisualState>

En el caso de estar en un dispositivo Windows Phone se lanzaría el
segundo estado visual, en el de Windows el primero, adaptando la
interfaz de usuario (en nuestro ejemplo un simple TextBlock).

De igual forma podemos crear Adaptive Triggers personalizados para
gestionar aspectos como el tipo de dispositivo, el tipo de orientación,
haciendo verificaciones de condiciones como datos, internet, etc.

Podéis descargar el 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] Control RelativePanel

Ordenando los elementos de la interfaz

Tenemos una gran diversidad de elementos visuales que nos permiten
definir la interfaz de usuario de nuestras Apps. Entre las opciones
disponibles tenemos un grupo especial destinado a organizar otros
elementos, los paneles.

Hasta ahora contábamos con el siguiente listado de paneles XAML en Windows y Windows Phone:

  • Grid: Nos permite crear filas y columnas para organizar los elementos visuales.
  • StackPanel:
    En inglés la palabra “stack” significa apilar o amontonar. Este control
    nos apila los elementos visuales que contiene verticalmente u
    horizontalmente.
  • Canvas: Podemos posicionar los elementos mediante coordenadas X-Y. Utilizamos posiciones absolutas.
  • ScrollViewer: Permite contener elementos más alla de los límites de la pantalla acceciendo a los mismos realizando scroll.
  • Border: Permite dibujar un borde alrededor de otro elemento visual.
  • ViewBox: Permite reescalar el contenido para rellenar el espacio disponible.
  • WrapGrid: Posiciona los elementos hijos secuencialmente de izquierda a derecha y de arriba a abajo.

Adaptando la interfaz

Adaptar la interfaz de usuario a distintos tamaños, orientaciones y
otros cambios no es algo nuevo. Con la llegada de las Apps Modern UI en
Windows 8.0 debíamos gestionar la vista Narrow. Con las Apps Universales
en Windows 8.1 desarrollábamos Apps con Windows Phone y Windows en
mente. Ante este tipo de situaciones, hasta ahora hemos utilizado
diferentes técnicas. Las más habituales son:

  • Vistas XAML separadas por plataforma.
  • Diseño flexible. Utilizar posiciones y tamaños relativos para permitir escalar facilmente.
  • Utilizar diferentes estados visuales (Visual States) para gestionar vistas en diferentes dispositivos, pantallas e incluso orientaciones.

En este artículo vamos a analizar un nuevo panel llamado Relative Panel. 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.

El control RelativePanel

Con la llegada del SDK de Windows 10 Preview nos llega el Panel Relative Panel. Permite:

  • Posicionar elementos con respecto al panel con múltiples posiciones.
  • Alinear elementos con respecto a sus “hermanos”. Podemos posicionar
    elementos visuales de manera relativa con respecto a otros elementos
    también con distintas opciones para posicionar segun nuestras
    necesidades.

Posicionando elementos

Para probar las posibilidades del nuevo panel crearemos un nuevo proyecto UAP:

Nuestro objetivo en el ejemplo sera añadir múliples rectángulos que
posicionaremos utilizándo todas las opciones disponibles que nos otorga
RelativePanel.

En nuestra vista principal añadimos el Panel:

<RelativePanel>
</RelativePanel>

Comenzamos añadiendo dos rectángulos de 100 x 200 px posicionándolos con respecto al RelativePanel:

<RelativePanel>
     <Rectangle x:Name="BlueRect"
        Height="100"
        Width="200"
        Fill="Blue" />
     <Rectangle x:Name="RedRect"
        Height="100"
        Width="100"
        Fill="Red"
        RelativePanel.AlignHorizontalCenterWithPanel="True"
        RelativePanel.AlignVerticalCenterWithPanel="True" />
</RelativePanel>

Analizamos el trozo de XAML anterior. Podemos posicionar los
elementos visuales dentro del RelativePanel con una serie de propiedades
adjuntas:

  • RelativePanel.AlignLeftWithPanel: Alineación a la izquierda del Panel.
  • RelativePanel.AlignRightWithPanel: Alineado a la derecha del Panel.
  • RelativePanel.AlignTopWithPanel: Alineado en la parte superior del Panel.
  • RelativePanel.AlignBottomWithPanel: Alineado en la parte inferior del Panel.
  • RelativePanel.CenterInPanelHorizontally: Alineado horizontalmente en el centro del Panel.
  • RelativePanel.CenterInPanelVertically: Alineado verticalmente en el centro del Panel.

Por defecto se aplican las propiedades
AlignLeftWithPanel y AlignTopWithPanel, por ese motivo el rectángulo
azul se posiciona en la parte superior izquierda de la ventana. En
cuanto al cuadrado rojo, lo posicionamos en el centro del panel tanto
horizontalmente como verticalmente.

Además de posicionar elementos con respecto al Panel, podemos posicionar
elementos de manera relativa a otros elementos. En nuestro ejemplo,
vamos a posicionar un nuevo rectángulo con respecto a otro:

<RelativePanel>
     <Rectangle x:Name="BlueRect"
        Height="100"
        Width="200"
        Fill="Blue" />
     <Rectangle x:Name="GreenRect"
        Height="100"
        Width="100"
        Fill="Green"
        RelativePanel.RightOf="BlueRect"
        RelativePanel.AlignVerticalCenterWith="BlueRect" />
     <Rectangle x:Name="RedRect"
        Height="100"
        Width="100"
        Fill="Red"
        RelativePanel.AlignHorizontalCenterWithPanel="True"
        RelativePanel.AlignVerticalCenterWithPanel="True" />
</RelativePanel>

El resultado:

Repasemos el último código añadido. Hemos añadido un nuevo rectángulo
verde que se posiciona a la derecha del rectángulo previo azul. Para
ello, se utilizan las propiedades adjuntas RelativePanel.RightOf y RelativePanel.AlignVerticalCenterWith.
La primera propiedad adjunta posiciona al elemento visual a la derecha
del elemento indicado mientras que la segunda lo alinea de forma
centrada verticalmente.

Tenemos una gran variedad de opciones para posicionar un elemento visual con respecto a otro:

  • RelativePanel.Above: Posiciona encima del elemento visual indicado.
  • RelativePanel.Below: Posiciona debajo del emento visual indicado.
  • RelativePanel.LeftOf: Posiciona a la izquierda del elemento visual indicado.
  • RelativePanel.RightOf: Posiciona a la derecha del elemento visual indicado.

En cuanto a establecer la alineación, de nuevo, contamos con una gran diversidad de opciones:

  • RelativePanel.AlignTopWith: Alineación con respecto a la parte superior del elemento visual indicado.
  • RelativePanel.AlignRightWith: Alineación con respecto a la  derecha del elemento visual indicado.
  • RelativePanel.AlignBottomWith: Alineación con respecto a la parte inferior del elemento visual indicado.
  • RelativePanel.AlignLeftWith: Alineación con respecto a la  izquierda del elemento visual indicado.
  • RelativePanel.AlignHorizontalCenterWith: Alineación central de forma horizontal con respecto elemento visual indicado.
  • RelativePanel.AlignVerticalCenterWith: Alineación central de forma vertical con respecto elemento visual indicado.

Continuamos con otro rectángulo posicionado de forma relativa con
respecto al rectángulo inicial azul. En este caso, vamos a posicionar el
rectángulo debajo y de forma centrada:

<RelativePanel>
     <Rectangle x:Name="BlueRect"
        Height="100"
        Width="200"
        Fill="Blue" />
     <Rectangle x:Name="GreenRect"
        Height="100"
        Width="100"
        Fill="Green"
        RelativePanel.RightOf="BlueRect"
        RelativePanel.AlignVerticalCenterWith="BlueRect" />
     <Rectangle x:Name="YellowRect"
        Height="100"
        Width="100"
        Fill="Yellow"
        RelativePanel.Below="BlueRect"
        RelativePanel.AlignHorizontalCenterWith="BlueRect" />
     <Rectangle x:Name="RedRect"
        Height="100"
        Width="100"
        Fill="Red"
        RelativePanel.AlignHorizontalCenterWithPanel="True"
        RelativePanel.AlignVerticalCenterWithPanel="True" />
</RelativePanel>

El resultado:

Podéis descargar el 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.

Conclusiones

Este nuevo panel llega con un firme objetivo, permitir posicionar de
forma sencilla permitiendo crear interfaces adaptativas a las distintas
plataformas. El uso de RelativePanel junto a los AdaptiveTriggers (y
junto al nuevo control SplitPanel) formarán una base muy comun en una
gran cantidad de Apps UAP.

Más información

[Windows 10] Disponibles las herramientas de desarrollo de Windows 10 Preview!

Introducción

El pasado 23 de Marzo, Microsoft liberaba dentro del programa de
Windows Insider una Preview de las herramientas de desarrollo de Windows
10.

Gallo-blog-2v2

Prerequisitos

Para poder utilizar las herramientas de desarrollo de Windows 10 Technical Preview es necesario tener instalado:

  • Windows 10 Preview en su versión más reciente.
  • Visual Studio 2015 CTP 6.
  • Herramientas de Windows 10 Technical Preview.

Además son necesarios al menos los siguientes requisitos de hardware:

  • Un procesador que cuente con Second Level Address Translation
    (SLAT). Básicamente todos los procesadores i3/i5/i7 cuentan con ello.
  • 4GB de RAM o superior.

¿Que ocurre si solo dispongo de mi inseparable portátil con un dual core?

Si tras verificar que tu procesador no cuenta con SLAT no desesperes.
Podrás desarrollar aplicaciones para Windows 10  solo que sin poder
ejecutar los emuladores de Windows Phone. Si podrás desplegar Apps en tu
propia equipo de desarrollo o en el simulador de Windows.

NOTA: Puedes verificar si tu procesador cuenta con SLAT y DEP utilizando una herramienta de Microsoft, llamada Coreinfo.

Instalación

Comenzamos entrando con nuestra cuenta en el programa de Windows Insider.

Windows Insider

Windows Insider

NOTA: Si aun no tenéis cuenta, a que esperáis!. Podéis crear una cuenta desde este enlace.

Debemos instalar o actualizar a la última versión de Windows 10 Technical Preview (ahora mismo la 10041) para poder soportar el desarrollo de Apps Universales Windows (UAP).

Buscar actualizaciones

Buscar actualizaciones

NOTA: Si cuentas con Windows 10 Technical Preview comprueba actualizaciones para asegurar que estas en la última versión.

El siguiente paso es instalar Visual Studio 2015 CTP6. Lo podemos instalar desde instalador Web o utilizando la ISO.
Es importante contar con esta versión, si tenéis Visual Studio 2015
CTP5 o anterior debemos desinstalarlo antes de instalar la CTP6.

Visual Studio 2015 CTP6

Visual Studio 2015 CTP6

Por último, procedemos a instalar las herramientas de desarrollo de Windows 10 Technical Preview.

Descarga de las herramientas de desarrollo de Windows 10 Technical Preview

Descarga de las herramientas de desarrollo de Windows 10 Technical Preview

Podemos descargar las herramientas utilizando el instalador Web o utilizando la ISO. Tras la descarga la instalación sera la habitual:

Tool for Windows 10

Tool for Windows 10

Llegado a este punto tenemos todo preparado para comenzar a crear
Apps Windows. Tened en cuenta que es una versión Preview, tendremos
muchas más novedades en el //BUILD. Os recomiendo leer los errores y problemas conocidos con la CTP6 de Visual Studio además de las notas relacionadas con las herramientas de desarrollo de Windows 10 Technical Preview.

Permaneced atentos al blog, en próximos artículos iremos analizando novedades en cuanto a herramientas, controles, APIs, etc.

Más información

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

Introducció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.

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

[Xamarin.Forms] Soporte para Apps Windows Runtime!

Introducción

Xamarin.Forms es un toolkit que nos crea una abstracción sobre la interfaz de usuario de Android, iOS y Windows Phone permitiendo desarrollar la interfaz de usuario una única vez con código C# o Extensible Application Markup Language (XAML).
Teníamos esta posibilidad con Apps Android, iOS y Windows Phone. Sin
embargo, en el caso de Windows Phone solo se soportaba Silverlight hasta
la versión 8.0. Con la llegada de la versión 8.1 se incorporaron las
aplicaciones WinRT que no estaban soportadas… hasta ahora!

En el marco de la dotNetConf 2015, Xamarin ha anunciado la disponibilidad de la versión Windows Preview. Se añade soporte a:

  • Apps Windows Store: Proyectos Windows destinados a Tabletas.
  • Apps Windows Phone 8.1: Proyectos WinRT Windows Phone 8.1.

En este artículo vamos a crear una App Xamarin.Forms con soporte a Windows y Windows Phone 8.1.

¿Te apuntas?

Preparando la Solución

El soporte en versión Preview de Windows en Xamarin.Forms se ha añadido incluyendo una nueva librería que podemos obtener vía NuGet.
Por lo tanto, en el proyecto creado podemos mantener el paquete NuGet
de Xamarin.Forms añadido, bastará con añadir otro paquete además de
cambiar los tipos de proyectos soportados en la librería PCL (en caso de
usar PCL en lugar de proyecto Shared).

NOTA: La nueva librería no esta aun incluida en
el paquete de Xamarin.Forms al estar en una versión temprana. En futuras
versiones más estables será incorporada.

Crearemos un nuevo proyecto Xamarin.Forms:

 Añadiendo el proyecto Windows

Vamos a comenzar añadiendo el proyecto de la App Windows Store. Clic derecho sobre la solución, nuevo proyecto:

Dentro de la categoría de proyectos de Aplicaciones de la tienda, seleccionamos una App vacía Windows.

Una vez creada, añadimos la referencia a la PCL:

Continuamos añadiendo una referencia al paquete Xamarin.Forms Windows
utilizando NuGet. Para ello, hacemos clic derecho sobre las referencias,
Administrar paquetes NuGet…

Buscamos por “Xamarin.Forms Windows” e instalamos la primera opción.

NOTA: Al instalar Xamarin.Forms Windows se nos añade también las librerías necesarias de Xamarin.Forms.

Ahora llega el momento de preparar el proyecto con leves cambios para utilizar la infraestructura de Xamarin.Forms.

Comenzamos añadiendo en el archivo App.xaml.cs la llamada al método Init en el método OnLaunched:

Xamarin.Forms.Forms.Init (e);

Editamos el tipo de la página principal MainPage.xaml:

forms:WindowsPhonePage

Donde el namespace de forms es:

xmlns:forms="using:Xamarin.Forms.Platform.WinRT"

Suprimimos el tipo Page en MainPage.xaml.cs:

public sealed partial class MainPage

Y en el contructor de la página llamamos al método LoadApplication:

LoadApplication(new Xamarin.Forms_Windows.App());

Y casi todo listo. Basta con revisar el archivo de manifiesto para
asegurar tener marcadas las capacidades de Internet y Localización.

Añadiendo el proyecto Windows Phone

Añadimos el proyecto Windows Phone 8.1:

Al igual que en el caso de Windows añadimos la referencia a la PLC y vía NuGet a Xamarin.Forms Windows.

Al igual que en el proyecto Windows, añadimos en el archivo App.xaml.cs la llamada al método Init en el método OnLaunched:

Xamarin.Forms.Forms.Init (e);

Editamos el tipo de la página principal MainPage.xaml:

forms:WindowsPhonePage

Donde el namespace de forms es:

xmlns:forms="using:Xamarin.Forms.Platform.WinRT"

Suprimimos el tipo Page en MainPage.xaml.cs:

public sealed partial class MainPage

Y en el contructor de la página llamamos al método LoadApplication:

LoadApplication(new Xamarin.Forms_Windows.App());

Todo listo!

Actualizando la PCL (en caso necesario)

Una vez añadidos los dos nuevos proyectos debemos actualizar el
perfil de la PCL. En el proyecto de la misma, hacemos clic derecho y
accedemos a sus propiedades. Veremos algo como:

Pulsamos el botón cambiar y añadimos Windows Phone 8.1:

NOTA: Para poder utilizar los proyectos Windows y
Windows Phone bajo WinRT en Xamarin.Forms se requiere Visual Studio
2013 o superior bajo Windows 8.1.

Comenzando!

Llegados a este punto nuestra solución Xamarin.Forms contará con la siguiente estructura:

Con Windows Preview contamos con dos proyectos más para añadir soporte a Apps Windows y Windows Phone 8.1.

En esta ocasión vamos a retomar un antiguo conocido muy simple
actualizando con el soporte a las nuevas plataformas. Mostraremos un
listado de monos en todas las plataformas utilizando el patrón MVVM.

Comenzamos creando una entidad muy sencilla dentro de la carpeta Models:

public class Monkey
{
     public string Name { get; set; }
     public string Image { get; set; }
     public string Location { get; set; }
     public string Details { get; set; }
}

En nuestra ViewModel definimos una propiedad con  la colección de monos que mostraremos en la interfaz de usuario:

public ObservableCollection<Monkey> Monkeys { get; set; }

Para mantener el enfoque en las nuevas plataformas, reduciremos
complejidad al ejemplo cargando el listado de datos directamente de
datos locales en nuestra ViewModel:

Monkeys = new ObservableCollection<Monkey>
{
     new Monkey
     {
          Name = "Baboon",
          Location = "Africa & Asia",
          Details = "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",
     },
     new Monkey
     {
          Name = "Capuchin Monkey",
          Location = "Central & South America",
          Details = "The
capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior
to 2011, the subfamily contained only a single genus, Cebus."
,
     },
     ...
};

Ya con la ViewModel preparada llega el turno de definir la interfaz de usuario. Crearemos una interfaz XAML definida dentro de la carpetas Views en nuestra PCL:

<ListView x:Name="List"
          ItemsSource="{Binding Monkeys}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell Height="50">
          <Grid Padding="5">
            <Grid.RowDefinitions>
              <RowDefinition Height="15" />
              <RowDefinition Height="15" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>            
              <ColumnDefinition Width="50"/>
              <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>     
            <Image
              Grid.Row="0"
              Grid.RowSpan="2"
              Grid.Column="0"
              Source="{Binding Image}"
              Aspect="AspectFill" />
            <Label
              Grid.Row="0"
              Grid.Column="1"
              Text="{Binding Name}"
              LineBreakMode="TailTruncation" />
            <Label
              Grid.Row="1"
              Grid.Column="1"
              Text="{Binding Location}"
              TextColor="Gray"
              LineBreakMode="TailTruncation" />
          </Grid>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Nuestra App esta lista para funcionar en tabletas Windows además de contar con versión Windows Phone 8.1:

NOTA: En la imagen anterior no aparecen pero seguimos por supuesto contando con App para Windows Phone 8.0 Silverlight e iOS.

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.

Conclusiones

Estamos ante una versión Preview y tenemos aun varias limitaciones:

  • No contamos aun con soporte a Mapas.
  • Nos falta el control GridView. En el caso de Apps Windows Store sin duda un control a echar de menos.
  • Algunos controles no se comportan exactamente igual y otros no tienen disponible todas las características.

A pesar de todo, esta versión Preview nos añade soporte para Apps
WinRT en tabletas Windows y en teléfonos Windows Phone 8.1 que era una
de las peticiones quizás más comunes. Es un gran paso adelante y no me
cabe ninguna duda que en los próximos meses (¿alguien dijo //BUILD?) tendremos nuevas versiones añadiendo características.

Más información

[Xamarin.Foms] Pull To Refresh

Novedades de Xamarin.Forms 1.4

La semana pasada Xamarin liberó la versión 1.4.0 de Xamarin.Forms. Una nueva versión con la celeridad a la que nos tienen acostumbrados y cargada de novedades interesantes:

  • Novedades en el ScrollView tanto a nivel de
    propiedades como de evnetos destinados a determinar cuando se realiza
    scroll, mover el scroll a una posición concreta, etc.
  • Novedades en el ListView con nuevas opciones como Header y Footer además de incluir la opció de gestionar Pull To Refresh.
  • Nuevos eventos añadidos a la clase Forms.Application para gestionar la navegación con modales.
  • Corrección de Bugs.

Pull To Refresh

El comportamiento “Pull To Refresh” es un gesto sencillo que permite generalmente refrescar
la información mostrada con un sencillo gesto. Inicialmente introducido
en Tweetie, cliente Twitter de iPhone, gracias a la facilidad de uso la
implementación del comportamiento se ha extendido en cientos de Apps.
De forma habitual consiste en un gesto de deslizamiento desde la parte
inferior hacia abajo, en ese momento se muestra feedback al usuario
indicando que se refrescará la información, de modo que, al levantar el
dedo se produce el refresco.

A pesar de todo, existen ciertas variantes en Apps conocidas en múltiples plataformas.

Pull To Refresh incluido en Xamarin.Forms 1.4.0

En Xamarin Classic y en Xamarin.Forms ya teníamos disponibles algunas
implementaciones para utilizar con todas las plataformas y por supuesto
también podíamos crearnos nuestra propia implementación utilizando
custom renderers. Ahora nos llega una implementación bastante completa y
sencilla directamente en las APIs de Xamarin.Forms:

public event EventHandler Refreshing;
  
public bool IsPullToRefreshEnabled { get; set; } = false;
public bool IsRefreshing { get; set; } = false;
public ICommand RefreshCommand { get; set; } = null;
  
public void BeginRefresh ();
public void EndRefresh ();

Podemos utilizar la propiedad IsPullToRefreshEnabled para habilitar en el ListView el comportamiento “Pull To Refresh”. Cuando el usuario lanza un Pull To Refresh el comando RefreshCommand. La propiedad IsRefreshing nos indica si se esta produciendo el refresco.

NOTA: Podemos establecer la propiedad IsRefreshing a false para terminar con el estado de refresco.

Contámos tambien con eventos para comenzar y detener el refresco. BeginRefresh y EndRefresh respectivamente.

Para probar las posibilidades crearemos un nuevo proyecto Xamarin.Forms:

Añadimos las carpetas Views y ViewModels en nuestra PCL. Añadimos la ViewModelBase y una implementación de DelegateCommand además de una vista XAML. En nuestra página añadiremos un ListView que será el utilizado para implementar Pull To Refresh:

<ListView>
</ListView>

En nuestra ViewModel necesitaremos una propiedad pública con la
colección de elementos a bindear al ListView. Además utilizaremos una
propiedad boolean para determinar si se está produciendo el refresco de
la información o no:

private ObservableCollection<string> _items;
 
public ObservableCollection<string> Items
{
    get { return _items; }
    set { _items = value; }
}
 
private bool _isBusy;
    
public bool IsBusy  
{
     get { return _isBusy; }
     set
     {
          _isBusy = value;
          RaisePropertyChanged();
     }
}

Continuamos añadiendo el comando que se ejecutará al realizar Pull To Refresh:

private DelegateCommandAsync _loadCommand;
 
public ICommand LoadCommand
{
     get { return _loadCommand ?? (_loadCommand = new DelegateCommandAsync(LoadCommandExecute)); }
}
 
private async Task LoadCommandExecute()
{
     await LoadData();
}

En la ejecución del comando se lanza el método LoadData:

private async Task LoadData()
{
     if (IsBusy)
          return;
 
     IsBusy = true;
 
     await Task.Delay(3000);
 
     for (int i = 0; i < 10; i++)
     {
          Items.Insert(0, _count.ToString());
          _count++;
     }
 
     IsBusy = false;
}

Para simplificar el ejemplo se cargan valores numéricos a la
colección. Se verifica que no se estén cargando datos, y se realiza la
carga de diez nuevos elementos.

NOTA: Se añade un retraso de 3 segundos en el método LoadData para simular el refresco desde cualquier servicio.

Nuestro ListView quedaría:

<ListView
     ItemsSource="{Binding Items}"
     HasUnevenRows="True"
     IsPullToRefreshEnabled="True"
     RefreshCommand="{Binding LoadCommand}"
     IsRefreshing="{Binding IsBusy, Mode=OneWay}">
</ListView>

Cargamos la información de la colección Items, permitiendo el comportamiento Pull To Refresh estableciendo la propiedad IsPullToRefreshEnabled a True, controlando si se esta realizando el refresco con la propiedad IsBusy y ejecutamos la lógica de refresco cargando más información mediante el comando LoadCommand.

Si ejecutamos la App, al arrancar cargará los diez primeros elementos:

Realizamos Pull To Refresh:

Se lanza el comando LoadCommand cargando diez nuevos elementos:

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] Universal Apps. Evento ItemClick a Comando

El 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:

De esta forma, tendremos nuestro GridView:

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

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