[Material dotNetSpain2016] No better time to be a .NET developer!

microsoft_.net_logoHace un año…

Tenía lugar la dotNet Spain Conference. Fueron dos días, 27 y 28 de Febrero del 2015 convirtiéndose en el mayor evento de .NET cubriendo todo el espectro, desde .NET MicroFramework a desarrollo de Apps, Cloud, IoT, soluciones ERP, etc. Sin duda un éxito rotundo con más de 1100 asistentes y grandes sensaciones. Motivos suficientes para no sorprenderse ante el regreso este año del mayor evento .NET del año, con más sesiones, sorpresas y todo lo necesario para volver a ser un enorme éxito.

Cifras de vértigo

Microsoft celebraba el pasado 24 de Febrero en Kinépolis Ciudad de la Imagen de Madrid, la dotNet Conference 2016. El evento más grande para la comunidad .NET del año. Y vaya si la comunidad ha respondido. Con más de 1700 asistentes y nada menos que 5000 seguidores vía streaming, el evento vuelve a ser este año un éxito aún mayor que el año anterior.

El evento

Completo de grandes sesiones técnicas y sorpresas, el evento comenzó con la keynote de Jose Bonnin. En ella se realizó un repaso de lo que depara el futuro de la tecnología en general y la implicación de .NET en ellas.

Keynote Jose Bonnín
Keynote Jose Bonnín

Tras la keynote, tuvimos las primeras 8 sesiones técnicas en paralelo justo antes del «gran momento», ese momento que fue durante un periodo una sorpresa y acabo resultando nada más y nada menos que la keynote de Satya Nadella, CEO de Microsoft.

"No hay mejor momento para ser desarrollador .NET" Satya Nadella
«No hay mejor momento para ser desarrollador .NET» Satya Nadella

Tras la keynote continuamos con hasta 32 sesiones técnicas más!.

Los Stands

El evento no hubiese sido posible sin la gran participación de empresas donde algunos stands mostraban proyectos o demos bastante interesantes (Oculus, drones, IoT, etc.) además de algún concurso divertido en el conseguir algún premio extra. Mención especial al espectacular stand que tuvimos en Plain Concepts donde causó sensación el divertido concurso con preguntas de C# y .NET:

¿Cuántas acertaste?
¿Cuántas acertaste?

También bastante interesante el Stand de Microsoft donde se podían probar los últimos Lumias pero además había apartados donde poder hablar con MSPs, resolver dudas con MVPs, etc.

Sorpresa final!

Jose Bonnin en sy Keynote ya anunció que tendríamos una conferencia vía Skype a última hora de la tarde. Llegado el momento se conectó vía Skype nada menos que con Scott Hanselman, Scott Hunter y David Carmona, para anunciar que Microsoft había alcanzado un acuerdo con Xamarin para realizar el proceso de compra!

Xamarin se une a Microsoft!
Xamarin se une a Microsoft!

Vaya forma más bestial de terminar un evento, que ya hasta ese momento había sido genial en organización y contenido.

¿No pudiste asistir?

Si no pudiste asistir, te agradará saber que tanto las keynotes como las sesiones del track web están disponibles en Channel 9urante el evento Channel 9. También se está recopilando el material de cada sesión del evento.

El material

He tenido en placer de poder participar en el evento con una sesión acerca de productividad en Xamarin en el Track de desarrollo Cross Platform.

En la sesión
En la sesión

La presentación utilizada disponible en SlideShare.

Comenzamos la sesión con una demo realizada en Xamarin.Forms 2.0 utilizando:

  • Animaciones
  • Custom Renders
  • Estilos y plantillas
  • Novedades de Xamarin.Forms 2.0 como XAML compilado, etc.
dotNetSpain2016
dotNetSpain2016

El objetivo era agrupar las últimas características de Xamarin.Forms en una aplicación para poder mostrar el uso Gorilla Player. Creado por UXDivers, el objetivo principal de Gorilla Player es permitir ver cualquier cambio visual en layouts, estilos o imágenes de forma inmediata sin necesidad de compilar.

Tras realizar modificaciones de Layout, estilos, pruebas con datos de prueba o la detección de errores de marcado, pasamos a inspeccionar la aplicación a fondo con Xamarin Inspector. Realizamos algunas pruebas al vuelo como peticiones a servicios web, pruebas con colores o modificaciones de elementos visuales para terminar detectando problemas en UI en la aplicación anterior y dando solución a las mismas.

La siguiente demo fue realizada en directo. Utilizamos Continuos Coding de Frank A. Krueger. La propuesta de Continuous Coding es reducir al máximo el ciclo de compilar y ejecutar permitiendo en tiempo real evaluar el código tanto de la lógica como por puesto a nivel de UI. Poder contruir la interfaz al vuelo viendo directamente los resultados de cada línea aplicada además de poder enlazar la misma con la lógica y el backend segun vamos escribiendo.

Así que, ni cortos ni perezosos, creamos un nuevo proyecto donde realizamos paso a paso una aplicación que mostrase la información meteorológica del tiempo en Madrid. Desde la petición al servicio a la definición de la interfaz, sin recompilar y viendo en todo momento información relacionada con excepciones o alertas relacionadas con nuestro código.

Continuamos hablando de calidad. Utilizando una aplicación completa de una calculadora:

Nuestra calculadora
Nuestra calculadora

Vimos como no solo crear pruebas unitarias sino también de UI. Tras utilizar el REPL pasamos a utilizar Xamarin Test Recorder viendo como simplificar enormemente el proceso de creación de los tests. También utilizamos Xamarin Test Cloud para pasar las pruebas en varios dispositivos viendo resultados.

Y para terminar, utilizamos Visual Studio Team Services en conjunto a MacInCloud y HockeyApp para realizar integración continua además de entrega continua de nuestras aplicaciones Xamarin.

CI en Android
CI en Android

Una sesión bastante completa, la hora y 15 minutos dan para poder interactuar más con los asistentes y permitir el lujo de tirar muchas líneas en directo. Otro acierto en mi opinión la duración.

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

Ver GitHub

Por el feedback recibido y correos con dudas parece que gustó mucho.

Me gustaría terminar agradeciendo a organizadores, ponentes y asistentes por hacer el posible el evento. Ante la cantidad y calidad de sesiones, los grandes momentos de networking, sorpresas, concursos y grandes momentos, no puedo terminar de otra forma que no sea…

¿Repetimos?

Más información

[VideoBlog] Acelerando el desarrollo en Xamarin

Introducción

Cada vez que nos llega a los desarrolladores un nuevo SDK, es un momento especial con una mezcla de altísima curiosidad y ganas de probar novedades. Entre las novedades principales siempre hay nuevas APIs, controles y otros elementos para poder realizar Apps que antes no eran posibles. Sin embargo, entre el conjunto de novedades siempre suelen venir nuevas herramientas que facilitan ciertas tareas: obtener más analíticas, mejores medidores de rendimiento, crear pruebas de UI, etc.

A parte de las herramientas oficiales que nos llegan por otro lado, la incansable comunidad sigue su ritmo lanzando su propias herramientas. En el VideoBlog de hoy nos centramos en las últimas herramientas más destacadas que llegan de la comunidad para facilitarnos la vida y aumentar nuestra productividad.

Continuous Coding, Xamarin.Forms Player y Gorilla Player

Trataremos tres herramientas diferentes:

  • Xamarin.Forms Player: El objetivo principal es permitir ver cualquier cambio visual en layouts, estilos o imágenes de forma inmediata sin necesidad de compilar.
  • Gorilla Player: Ver cambios de UI al vuelo en una App Xamarin.Forms además de poder usar datos de prueba, modificar estilos o detectar errores de marcado XAML.
  • Continuous Coding: La propuesta es reducir al máximo el ciclo de compilar y ejecutar permitiendo en tiempo real evaluar el código tanto de la lógica como por puesto a nivel de UI. Poder contruir la interfaz al vuelo viendo directamente los resultados de cada línea aplicada además de poder enlazar la misma con la lógica y el backend segun vamos escribiendo.

Recordar que cualquier sugerencia o duda la podéis dejar en los comentarios tanto del blog como del video.

Más información

[Xamarin.Forms] Diferentes plantillas en listado, utilizando DataTemplateSelectors

Black List-WFLlega Xamarin.Forms 2.1

Tras el lanzamiento de Xamarin.Forms 2.0 con grandes novedades como el soporte Preview de UWP o las importantes mejoras en rendimiento, todo continua su curso y tenemos ya disponible Xamarin.Forms 2.1. Contamos con las siguientes mejoras:

  • Efectos: Los efectos llegan como una nueva forma de personalizar aspecto y comportamiento de controles sin necesidad de realizar Custom Renders.
  • ControlTemplates: TemplatedPage y TemplatedView sirven ahora como clase base para ContentPage y ContentView. Se pueden usar para definir la apariencia de un control o página manteniendo una clara separación entre la jerarquía visual y el contenido.
  • DataTemplateSelectors: Poder elegir en tiempo de ejecución la plantilla a utilizar en cada elemento un listado.
  • Otros: Añadida virtualización en UWP, corrección de bugs, etc.

En este artículo vamos a centrarnos en el uso de DataTemplateSelectors en Xamarin.Forms.

DataTemplateSelectors

El objetivo de nuestro ejemplo será mostrar diferentes elementos a nivel visual en un listado. Por ejemplo, en un listado mostrando las actividades del día para una agenda, mostrar de forma visual los elementos de forma diferenciada cada tipo de actividad.

Vamos a crear un ejemplo con el ya utilizado previamente listado de monos. Algunos de ellos contarán con foto disponible mientras que otros no. Vamos a utilizar una plantilla diferente en cada caso.

Comenzamos creando en la viewmodel de la vista una colección con nuestros monos:

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

Para centrar la atención absoluta en el uso de diferentes plantillas, la carga de monos la haremos de la forma más simple posible:

private void LoadMonkeys()
{
     Monkeys = new ObservableCollection<Monkey>();

     Monkeys.Add(new Monkey
     {
          Name = "Baboon",
          Description = "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",
          Image = "http://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_anubis_%28Serengeti%2C_2009%29.jpg/200px-Papio_anubis_%28Serengeti%2C_2009%29.jpg"
     });

     Monkeys.Add(new Monkey
     {
          Name = "Capuchin Monkey",
          Description = "The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus."
     });

     Monkeys.Add(new Monkey
     {
          Name = "Blue Monkey",
          Description = "The blue monkey or diademed monkey is a species of Old World monkey native to Central and East Africa, ranging from the upper Congo River basin east to the East African Rift and south to northern Angola and Zambia"
     });

     ...
}

Creamos directamente una colección local. Como podemos ver, cada mono esta representado por nombre, descripción y foto. Sin embargo, algunos de ellos no cuentan con foto. En nuestra interfaz:

<ListView
     x:Name="list"
     ItemsSource="{Binding Monkeys}"
     CachingStrategy="RecycleElement">
</ListView>

Utilizaremos un ListView bindeado a la colección de monos.

NOTA: Desde Xamarin.Forms 2.0, el control ListView recibe una nueva propiedad llamada CachingStrategy para esteblecer el tipo de cacheo realizado.

Para la representación de elementos podemos utilizar la propiedad ItemTemplate del control junto a una plantilla. En nuestro caso, deseamos que los elementos con foto se muestren de forma diferente a cuando no contamos con foto.

Crearemos dos plantillas. Por un lado la plantilla para elementos con foto:

<DataTemplate x:Key="MonkeyTemplate">
     <ViewCell Height="200">
          <ViewCell.View>
            <Grid BackgroundColor="#d3d3d3">
              <Grid.RowDefinitions>
                <RowDefinition Height="20" />
                <RowDefinition Height="30" />
              </Grid.RowDefinitions>
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="40" />
              </Grid.ColumnDefinitions>
              <Label Grid.Row="0" Grid.Column="0"
                     Text="{Binding Name}" LineBreakMode="TailTruncation" />
              <Label Grid.Row="1" Grid.Column="0"
                     Text="{Binding Description}" Font="Small" TextColor="Gray" 
                     LineBreakMode="TailTruncation" />
              <Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"
                     Source="{Binding Image}" Aspect="AspectFill" />
            </Grid>
          </ViewCell.View>
     </ViewCell>
</DataTemplate>

Sin foto:

<DataTemplate x:Key="NoPhotoTemplate">
     <ViewCell>
          <ViewCell.View>
            <StackLayout BackgroundColor="#d3d3d3">
              <Label Text="{Binding Name}" HorizontalOptions="End" />
              <Label Text="{Binding Description}" HorizontalOptions="Start"
                     Font="Small" TextColor="Gray" />
            </StackLayout>
          </ViewCell.View>
     </ViewCell>
</DataTemplate>

Llega el momento de añadir la lógica específica para asociar la plantilla adecuada a cada elemento en tiempo de ejecución. Crearemos una clase derivada de DataTemplateSelector:

public class MonkeyDataTemplateSelector : DataTemplateSelector

La clave la clase será el método OnSelectTemplate. Este método se lanzará por cada elemento del listado y es donde añadiremos la lógica específica para seleccionar la plantilla correspondiente. En nuestro ejemplo, crearemos dos variables de tipo DataTemplate donde tendremos cada una de las plantillas previas creadas en los recursos de la aplicación.

public class MonkeyDataTemplateSelector : DataTemplateSelector
{
     private readonly DataTemplate _monkey;
     private readonly DataTemplate _noPhoto;

     public MonkeyDataTemplateSelector()
     {
         _monkey = (DataTemplate)App.Current.Resources["MonkeyTemplate"];
         _noPhoto = (DataTemplate)App.Current.Resources["NoPhotoTemplate"];
     }

     protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
     {
         if (item is Monkey)
         {
             if(!string.IsNullOrEmpty(((Monkey)item).Image))
                 return _monkey;
         }

         return _noPhoto;
     }
}

Verificamos que el tipo de objeto bindeado en cada elemento es de tipo Monkey y verificamos si cuenta con foto o no. Dependiendo de ello, devolveremos una plantilla u otra.

En nuestra interfaz, utilizaremos el selector de plantilla en la propiedad ItemTemplate:

<ListView
     x:Name="list"
     ItemsSource="{Binding Monkeys}"
     CachingStrategy="RecycleElement">
     <ListView.ItemTemplate>
       <template:MonkeyDataTemplateSelector/>
     </ListView.ItemTemplate>
</ListView>

Todo listo!. Sencillo, ¿cierto?. Si ejecutamos la aplicación:

DataTemplateSelector en Xamarin.Forms
DataTemplateSelector en Xamarin.Forms

Podemos ver como aquellos monos que no cuentan con foto, además de no mostrarla, su nombre se encuentra alineado a la derecha.

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

Ver GitHub

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

A tener en cuenta

Contamos con una serie de detalles y limitaciones a tener en cuenta:

  • En Android no podemos utilizar más de 20 plantillas por listado.
  • Un DataTemplateSelector no puede devolver otro.
  • El DataTemplateSelector no debe devolver una nueva instancia de cada DataTemplate en cada llamada, debería de usarse la misma.Cuidado con este punto si no deseamos obtener de forma sencilla Memory Leaks.

Más información

[VideoBlog] Animaciones en Xamarin.Forms

Animation02Introducción

En todas las plataformas, las aplicaciones móviles incluyen animaciones que otorgan movimiento, fluidez y focalizan la atención del usuario en las zonas deseadas. Actualmente no son un extra o añadido en las aplicaciones, sino una parte importante en la experiencia y usabilidad de las mismas.

Como desarrolladores, debemos no solo cuidar por supuesto el correcto funcionamiento de la aplicación, sino que debemos preocuparnos también por la usabilidad y la experiencia otorgada, donde entran en juego las animaciones.

Por ello, abordamos un nuevo VideoBlog totalmente centrado en el uso de animaciones en Xamarin.Forms.

Animaciones en Xamarin.Forms & Xamanimation

Xamarin.Foms incluye una serie de métodos de extensión que nos permiten realizar una completa gestión de animaciones desde código C#.

Por un lado tenemos disponibles una serie de animaciones predefinidas disponibles como métodos de extensión para elementos de tipo View. Contamos con animaciones para realizar rotaciones, escalados, tralaciones, etc. En caso de necesitar animaciones más complejas podemos utilizar las extensiones de animación. Contamos con varias sobrecargas del método Animate que nos permite definir animaciones más complejas.

Para complementar y expandir las posibilidades contamos con Xamanimation. Es es una librería gratuita destinada para Xamarin.Forms que tiene como objetivo facilitar el uso de animaciones a los desarrolladores. Añade un conjunto de animaciones de uso muy sencillo tanto desde código C# como desde código XAML. Podemos definir animaciones en XAML a un elemento visual al cargar mediante un Behavior, usar un trigger en XAML para ejecutar la animación a voluntad al lanzar el trigger o bien desde código C#.

Más información

VideoBlog, nuevo formato en el Blog!

YouTubeNovedad en el blog

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

Nuevo formato

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

Más información

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

Scale to Fit-02Introducción

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

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

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

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

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

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

DisplayInformation

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

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

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

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

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

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

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

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

private DisplayInformation _displayInformation;

_displayInformation = DisplayInformation.GetForCurrentView();

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

_displayInformation.DpiChanged += _displayInformation_DpiChanged;

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

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

     UpdateDpi(displayInformation);
}

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

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

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

Recursos por DPI

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

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

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

Ejemplos de escala
Ejemplos de escala

Añadiendo recursos por escala

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

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

Recursos por DPI
Recursos por DPI

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

Utilizando recursos por escala

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

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

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

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

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

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

Carga de Assets por DPI
Carga de Assets por DPI

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

Sencillo, ¿cierto?

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

Ver GitHub

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

Más información

[Xamarin.Forms] Animaciones y nueva librería

AnimationIntroducción

En todas las plataformas, las aplicaciones móviles incluyen animaciones que otorgan movimiento, fluidez y focalizan la atención del usuario en las zonas deseadas. Actualmente no son un extra o añadido en las aplicaciones, sino una parte importante en la experiencia y usabilidad de las mismas.

Como desarrolladores, debemos no solo cuidar por supuesto el correcto funcionamiento de la aplicación, sino que debemos preocuparnos también por la usabilidad y la experiencia otorgada, donde entran en juego las animaciones.

En este artículo vamos a profundizar a fondo en el uso de animaciones desde Xamarin.Forms.

Animaciones en Xamarin.Forms

Xamarin.Foms incluye una serie de métodos de extensión que nos permiten realizar una completa gestión de animaciones desde código C#.

Por un lado tenemos disponibles una serie de animaciones predefinidas disponibles como métodos de extensión para elementos de tipo View. Contamos con animaciones para realizar rotaciones, escalados, tralaciones, etc. Los métodos de extensión a utilizar son:

  • FadeTo: Podemos animar la opacidad de un elemento visual.
  • RelRotateTo: Podemos especificar un ángulo de rotación para realizar transformaciones de rotación.
  • RelScaleTo: Transformación de escalado.
  • RotateTo: Transformación de rotación.
  • RotateXTo: Transformación de rotación.
  • RotateYTo: Transformación rotación.
  • ScaleTo: Transformación de escalado.
  • TranslateTo: Estableciendo las propiedades TranslationX y TranslationY podemos realizar traslaciones del elemento visual.

En caso de necesitar animaciones más complejas podemos utilizar las extensiones de animación. Contamos con varias sobrecargas del método Animate que nos permite definir animaciones más complejas.

Podemos crear un ejemplo sencillo donde probar tanto las animaciones predefinidas como las personalizadas. En la interfaz añadimos dos cuadrados (BoxView) con dos botones:

<StackLayout>
    <Label Text="Pre-defined Animations" />
    <BoxView
      x:Name="PreDefinedBox"
      HorizontalOptions="Center"
      VerticalOptions="Center"
      HeightRequest="125"
      WidthRequest="125"
      BackgroundColor="Red"/>
    <Button
      x:Name="PreDefinedButton"
      HorizontalOptions="Center"
      VerticalOptions="Center"
      Text="Animate"/>
    <Label Text="Custom Animations" />
    <BoxView
      x:Name="CustomBox"
      HorizontalOptions="Center"
      VerticalOptions="Center"
      HeightRequest="125"
      WidthRequest="125"
      BackgroundColor="Blue"/>
    <Button
      x:Name="CustomButton"
      HorizontalOptions="Center"
      VerticalOptions="Center"
      Text="Animate"/>
</StackLayout>

El primer botón animará la primera caja con animaciones predefinidas:

await PreDefinedBox.ScaleTo(2, 1000, Easing.CubicInOut);              
await PreDefinedBox.RotateTo(75, 1000, Easing.CubicInOut);                
await PreDefinedBox.ScaleTo(1, 1000, Easing.CubicInOut);

Realizamos un escalado al doble del tamaño, rotamos 75º y volvemos a escalar al tamaño original.

Sencillo, ¿verdad?

En caso de necesitar realizar animaciones personalizadas más complejas, podemos utilizar el método Animate:

CustomBox.Animate("Custom Animation", x => 
{
     CustomBox.BackgroundColor = Color.FromRgb(x, 0, 1 - x);
     CustomBox.Scale = 1 + 1.1 * x;                
}, length: 500);

Realizamos un cambio de color y escalado.

El resultado en ambos casos:

Animaciones en Xamarin.Forms
Animaciones en Xamarin.Forms

El ejemplo lo podéis encontrar en GitHub:

Ver GitHub

Xamanimation

Xamanimation es una librería destinada para Xamarin.Forms que tiene como objetivo facilitar el uso de animaciones a los desarrolladores. Añade un conjunto de animaciones de uso muy sencillo tanto desde código C# como desde código XAML.

Podemos definir animaciones en XAML a un elemento visual al cargar mediante un Behavior, usar un trigger en XAML para ejecutar la animación a voluntad al lanzar el trigger o bien desde código C#.

Animaciones disponibles

La librería cuenta una gran variedad de animaciones:

  • FadeTo
  • Flip
  • Heart
  • Jump
  • Rotate
  • Scale
  • Shake
  • Translate
  • Turnstile

Animando desde XAML

Una de las ventajas principales de la librería es la posibilidad de uso de animaciones desde XAML. Debemos utilizar el siguiente namespace:

xmlns:xamanimation="clr-namespace:Xamanimation;assembly=Xamanimation"

Al igual que en el ejemplo anterior, vamos a animar un cuadrado:

 <BoxView
     x:Name="FadeBox"
     HeightRequest="120"
     WidthRequest="120"
     Color="Blue" />

Tanto a nivel de recursos de aplicación como de vista, podemos definir animaciones:

<xamanimation:FadeToAnimation
     x:Key="FadeToAnimation"
     Target="{x:Reference FadeBox}"
     Duration="2000"
     Opacity="0"/>

Utilizando el namespace de xamanimation, tenemos acceso a todo el conjunto de animaciones disponible en la librería. En todas ellas hay una serie de parámetros comunes como:

  • Target: Nos permite indicar el elemento visual al que le aplicaremos la animación.
  • Duration: Duración de la animación en milisegundos.

Según el tipo de animación utilizada contaremos con más parámetros para personalizar la animación específica. Por ejemplo, en el caso de Fade contaremos con una propiedad Opacity para establecer como modificamos la opacidad.

Para lanzar la animación tenemos dos opciones:

  • Trigger: Llamado BeginAnimation que nos permite lanzar una animación al producirse una condición.
  • Behavior: Contamos con un Behavior llamado BeginAnimation que podemos asociar a un elemento visual de modo que que indicando la animación deseada, podemos lanzar la misma cuando se produzca la carga del elemento.

Utilizando el evento Clicked de un botón podemos lanzar la animación anterior utilizando el trigger facilitado por la librería:

<Button         
     Text="Fade">
     <Button.Triggers>
          <EventTrigger Event="Clicked">
               <xamanimation:BeginAnimation
                    Animation="{StaticResource FadeToAnimation}" />
          </EventTrigger>
     </Button.Triggers>        
</Button>

Sencillo, ¿cierto?. También contamos con el concepto de Storyboard como un conjunto de animaciones que podemos ejecutar a lo largo del tiempo:Animations 02

<xamanimation:StoryBoard
     x:Key="StoryBoard"
     Target="{x:Reference StoryBoardBox}">
       <xamanimation:ScaleToAnimation  Scale="2"/>
       <xamanimation:ShakeAnimation />
</xamanimation:StoryBoard>

El ejemplo anterior ejecutaría un escalado al doble del tamaño original y tras escalar realizaría una «agitación».

Animando desde C#

De igual forma que podemos utilizar las animaciones de la librería desde XAML, podemos hacerlo desde código C#. Contamos con un método de extensión llamado Animate que espera una instancia de cualquiera de las animaciones disponibles.

Si deseamos animar de nuevo un cuadrado llamado AnimationBox:

<BoxView
     x:Name="AnimationBox"
     HeightRequest="120"
     WidthRequest="120"
     Color="Blue" />

Bastará con acceder al elemento, utilizar el método Animate con la animación deseada:

AnimationBox.Animate(new HeartAnimation());

Podéis ver un subconjunto de las animaciones disponibles en acción a continuación:

Xamanimation
Xamanimation

El ejemplo lo podéis encontrar en GitHub:

Ver GitHub

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

¿Y que esperar de la librería?

El código fuente esta disponible en GitHub y hay libertad absoluta para realizar con el lo que estiméis oportuno. Si deseáis colaborar, sin duda se aceptarán Pull Request. La librería seguirá evolucionando próximamente con:

  • Más opciones en cada animación (modo repetición)
  • Más animaciones (Color, Wobble, etc.)
  • Y otras novedades!

Más información

[Windows 10] Experiencias multipantalla utilizando ProjectionManager

Introducción

Entre el conjunto de posibilidades nuevas
disponibles con Windows 10, sin duda alguna, hay una brilla fuertemente
sobre las demas, Continuum. Esta característica permite
conectar un teléfono a un monitor externo permitiendo interactuar con
la App en modo escritorio mientras podemos continuar utilizando el
teléfono.

Continuum
Continuum

Es
vital utilizar los nuevos AdaptiveTriggers, RelativePanel además de
controlar el modo de interacción y otros detalles para adaptar la
interfaz y usabilidad a cada posible situación. De esta forma
conseguimos aplicaciones adaptativas pudiendo ofrecer la experiencia
idónea en cada familia de dispositivo soportado.

En Continuum
podemos tener una única App en la pantalla secundaria de forma
simultánea. Sin embargo, podemos crear experiencias con múltiples
pantallas. ¿Os imagináis ver listado de restaurantes cercanos en el
teléfono mientras que en pantalla grande vemos mapa mostrando cercanía a
nuestra posisión y críticas?, ¿ tener detalles de una película y ver el
trailer de la misma en pantalla completa?. Escenarios donde sacar
partido de la proyección de información a una pantalla
secundaria hay muchos tanto en aplicaciones como en juegos. En este
artículo vamos a sacarle todo el partido a la clase ProjectionManager y el trabajo multipantalla.

ProjectionManager
ProjectionManager

Proyección de vistas

Crearemos un nuevo proyecto UAP:

Nueva App UAP
Nueva App UAP

Añadimos las carpetas Views, ViewModels y Services además de las clases base necesarias para implementar el patrón MVVM de la misma forma que vimos en este artículo.

El objetivo del artículo será proyectar una pantalla secundaria para aprender a:

  • Proyectar pantalla secundaria.
  • Detener la proyección.
  • Hacer un intercambio de la pantalla donde se proyecta.

Detectar pantalla secundaria

ProjectionManager
nos permite proyectar una ventana de nuestra App en una pantalla
secundaria. A nivel de desarrollo, el proceso es similar a trabajar con múltiples ventanas en la misma App. Para proyectar en otra pantalla lo primero que debemos verificar es si disponemos de esa pantalla.

En nuestro ejemplo, mostraremos en la interfaz si contamos o no con la pantalla donde proyectar:

<TextBlock 
     Text="{Binding IsProjectionDisplayAvailable}"/>

Usaremos una sencilla propiedad bool en la viewmodel:

private bool _isProjectionDisplayAvailable;

public bool IsProjectionDisplayAvailable
{
     get { return _isProjectionDisplayAvailable; }
     set
     {
          _isProjectionDisplayAvailable = value;
          RaisePropertyChanged();
     }
}

En la clase ProjectionManager contamos con el evento ProjectionDisplayAvailableChanged que se lanza cada vez que la pantalla secundaria sobre la que proyecta pasa a estar disponible o no disponible:

ProjectionManager.ProjectionDisplayAvailableChanged += ProjectionManager_ProjectionDisplayAvailableChanged;

También podemos realizar la verificación de si tenemos disponible la pantalla secundaris utilizando la propiedad ProjectionDisplayAvailable:

IsProjectionDisplayAvailable = ProjectionManager.ProjectionDisplayAvailable;

NOTA: Si no contamos con pantalla secundaria, la
vista proyectada se mostrará en la misma pantalla donde se encuentra la
vista principal.

Proyectar

Conocemos como verificar si contamos con pantalla secundaria sobre la que proyectar, veamos como realizar la proyección.

En nuestra interfaz tendremos un botón que nos permitirá proyectar una vista específica:

<Button
     Content="Project"
     Command="{Binding ProjectCommand}"/>

Nuestra interfaz principal:

Vista principal

Vista principal

El comando a ejecutar:

private ICommand _projectCommand;
 
public ICommand ProjectCommand
{
     get { return _projectCommand = _projectCommand ?? new DelegateCommandAsync(ProjectCommandExecute); }
}
 
public async Task ProjectCommandExecute()
{
     App.MainViewId = await _projectionService.ProjectAsync(typeof(ProjectionView));
}

Hemos creado un servicio ProjectionService en el que tenemos agrupada toda la lógica de proyección. Para proyectar utilizamos el siguiente método:

public async Task<int> ProjectAsync(Type viewType, DeviceInformation device = null)
{
     int mainViewId = ApplicationView.GetForCurrentView().Id;
     int? secondViewId = null;
 
     var view = CoreApplication.CreateNewView();
     await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
     {
          secondViewId = ApplicationView.GetForCurrentView().Id;
          var rootFrame = new Frame();
          rootFrame.Navigate(viewType, null);
          Window.Current.Content = rootFrame;
          Window.Current.Activate();
     });
 
     if (secondViewId.HasValue)
     {
          if(device == null)
              await ProjectionManager.StartProjectingAsync(secondViewId.Value, mainViewId);
          else
              await ProjectionManager.StartProjectingAsync(secondViewId.Value, mainViewId, device);
     }
 
     return mainViewId;
}

Para realizar la proyección utilizamos el método StartProjectingAsync(Int32,Int32) al que le pasamos como parámetros:

  • ProjectionViewId: El  identificador de la ventana que se va a mostrar en la pantalla secundaria.
  • AnchorViewId: El identificador de la ventana original.

Comenzamos creando una nueva vista vacía en blanca. En esta vista
navegamos a la vista que deseamos proyectar y la asignamos como
contenido. Podemos pasar los parámetros necesarios en este punto.

NOTA: Es totalmente necesario realizar la llamada a Window.Current.Activate para que la vista pueda visualizarse.

La vista no aparecerá hasta lanzar el método StartProjectingAsync.
Tras lanzarlo, colocamos una vista existente en una pantalla
secundaria, en caso de detectar una. De lo contrario, la vista se sitúa
en el monitor principal.

Proyectar seleccionando la pantalla

Lo visto hasta este punto es sencillo y efectivo. Sin embargo,
podemos tener situaciones más complejas con múltiples pantallas
sencundarias.

¿Podemos elegir sobre que pantalla proyectar?

Si, podemos. Vamos a ver como realizar este proceso. Creamos en la
interfaz otro botón de modo que al ser pulsado nos muestre todas las
pantallas disponibles. Una vez seleccionada una pantalla específica
proyectaríamos sobre la misma:

<Button
     Content="Select Target and Project"
     Command="{Binding SelectTargetCommand}"/>
<ListView
     ItemsSource="{Binding Devices}"
     SelectedItem="{Binding SelectedDevice, Mode=TwoWay}"
     Height="300"
     Width="300"
     HorizontalAlignment="Left">
     <ListView.ItemTemplate>
          <DataTemplate>
               <TextBlock Text="{Binding Name}" />
          </DataTemplate>
     </ListView.ItemTemplate>
</ListView>

En la viewmodel:

private ICommand _selectTargetCommand;
 
public ICommand SelectTargetCommand
{
     get { return _selectTargetCommand = _selectTargetCommand ?? new DelegateCommandAsync(SelectTargetCommandExecute); }
}
 
public async Task SelectTargetCommandExecute()
{
     try
     {
          Devices = new ObservableCollection<DeviceInformation>(await _projectionService.GetProjectionDevices());
     }
     catch (Exception ex)
     {
          Debug.WriteLine(ex.Message);
     }
}

Utilizamos el siguiente método:

public async Task<IEnumerable<DeviceInformation>> GetProjectionDevices()
{
     // List wired/wireless displays
     String projectorSelectorQuery = ProjectionManager.GetDeviceSelector();
 
     // Use device API to find devices based on the query
     var projectionDevices = await DeviceInformation.FindAllAsync(projectorSelectorQuery);
 
     var devices = new ObservableCollection<DeviceInformation>();
     foreach (var device in projectionDevices)
          devices.Add(device);
 
     return devices;
}

Utilizamos el método GetDeviceSelector disponible en ProjectionManager
que nos devuelve una cadena con la enumeración de dispositivos
disponibles. Utilizamos la cadena para obtener una colección de
dispositivos (DeviceInformation) en los cuales tenemos toda la información necesaria.

La colección obtenida es la que bindeamos a nuestra interfaz. Una vez seleccionado un dispositivo concreto:

private async Task Project(DeviceInformation device)
{
     try
     {
          // Show the view on a second display (if available)
          App.MainViewId = await _projectionService.ProjectAsync(typeof(ProjectionView), device);
 
          Debug.WriteLine("Projection started in {0} successfully!", device.Name);
     }
     catch (Exception ex)
     {
          Debug.WriteLine(ex.Message);
     }
}

Utilizamos un método al que le pasamos el dispositivo y se encarga de
realizar la proyección. En este caso, en nuestro servicio utilizamos el
método StartProjectingAsync(Int32,Int32,DeviceInformation)
donde además de los identificadores de la nueva vista y de la original,
indicamos el dispositivo, es decir, la pantalla específica sobre la que
proyectar.

También podemos de forma muy sencilla permitir elegir el dispositivo sobre el que proyectar utilizando el método RequestStartProjectingAsync(Int32,Int32,Rect,Placement).
Utilizando este método se mostrará un flyout con el listado de
dispositivos, de modo que, una vez seleccionado uno, comenzamos la
proyección. Para indicar la posición del flyout podemos utilizar un
parámetro de tipo Rect.

public async Task<int> RequestProjectAsync(Type viewType, Rect? position = null)
{
     int mainViewId = ApplicationView.GetForCurrentView().Id;
     int? secondViewId = null;
 
     var view = CoreApplication.CreateNewView();
     await view.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
     {
          secondViewId = ApplicationView.GetForCurrentView().Id;
          var rootFrame = new Frame();
          rootFrame.Navigate(viewType, mainViewId);
          Window.Current.Content = rootFrame;
          Window.Current.Activate();
     });
 
     if (secondViewId.HasValue)
     {
          var defaultPosition = new Rect(0.0, 0.0, 200.0, 200.0);
          await ProjectionManager.RequestStartProjectingAsync(secondViewId.Value, mainViewId, position.HasValue ? position.Value : defaultPosition);
     }
 
     return mainViewId;
}

La vista proyectada

¿Y que ocurre con la vista proyecta?. Nada en especial, puede ser
cualquier vista de la aplicación. Sin embargo, puede llegar a
interesarnos realizar algunas interacciones con la API de proyección
como:

  • Detener la proyección.
  • Modificar el dispositivo donde proyectamos.

La interfaz de usuario contará con dos botones, uno para detener la proyección y otro para modificar el dispositivo utilizado.

<Grid>
     <StackPanel>
          <TextBlock
              Text="Projection View"
              FontWeight="SemiBold"/>
          <Button
              Content="Swap"
              Command="{Binding SwitchViewCommand}"/>
          <Button
              Content="Stop"
              Command="{Binding StopCommand}"/>
     </StackPanel>
</Grid>

El resultado:

Vista proyectada

Vista proyectada

En la viewmodel:

private ICommand _switchViewCommand;
private ICommand _stopCommand;
 
public ICommand SwitchViewCommand
{
     get { return _switchViewCommand = _switchViewCommand ?? new DelegateCommandAsync(SwitchViewCommandExecute); }
}
 
public ICommand StopCommand
{
     get { return _stopCommand = _stopCommand ?? new DelegateCommandAsync(StopCommandExecute); }
}
 
public async Task SwitchViewCommandExecute()
{
     try
     {
          await _projectionService.SwitchProjection(App.MainViewId);
     }
     catch (Exception ex)
     {
          Debug.WriteLine(ex.Message);
     }
}
 
public async Task StopCommandExecute()
{
     try
     {
          await _projectionService.StopProjection(App.MainViewId);
     }
     catch (Exception ex)
     {
          Debug.WriteLine(ex.Message);
     }
}

Detener proyección

Para detener la proyección tenemos a nuestra disposición el método StopProjectingAsync que oculta la vista mostrada en proyector o pantalla secundaria.

public async Task StopProjection(int mainViewId)
{
     await ProjectionManager.StopProjectingAsync(       
                ApplicationView.GetForCurrentView().Id,
                mainViewId);
}

Cambiar pantalla

Podemos cambiar al vuelo la pantalla donde se realiza la proyección utilizando el método SwapDisplaysForViewsAsync de la clase ProjectionManager:

public async Task SwapProjection(int mainViewId)
{
     await ProjectionManager.SwapDisplaysForViewsAsync(
                ApplicationView.GetForCurrentView().Id,
                mainViewId);
}

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

Ver GitHub

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

Más información