[Windows Phone 8.1 TIP] Gestionar la barra de sistema

Hola a todos!

Existe un tema tan ignorado en el desarrollo de aplicaciones para Windows Phone, por parte de los propios desarrolladores, que incluso parece que se trate de algún tipo de tabú social auto impuesto. Hablo de la barra del sistema. esa pequeña franja de apenas 20 píxeles de alto, donde Windows Phone muestra la hora, la cobertura y otras notificaciones.

Generalizando, que nunca es bueno, encontramos dos formas típicas de gestionar la barra de sistema. Existen desarrolladores que simplemente no la gestionan. Ese espacio no es suyo, no les importa y no se molestan ni siquiera en mirarlo. Luego están los “acaparadores”, los que quieren cada píxel de la pantalla para ellos mismos. Estos le prestan atención a la barra del sistema el tiempo suficiente para comprobar que su código ha hecho efectivamente su trabajo: ocultarla para siempre.

Personalmente creo que ni unos ni otros tienen razón. Al menos no siempre. Como usuario, tener la barra de sistema con sus notificaciones y la hora, me conserva en la aplicación activa. Si tengo que salir de la aplicación solo por querer ver la hora o si tengo conexión WiFi, quizás no vuelva a entrar. Por otro lado, ver la barra de sistema con fondo negro y letras blancas, o viceversa, pegada a una app muy bien diseñada… es algo chocante… que hace deslucir toda la aplicación.

Y digo que es una pena, por lo extremadamente sencillo que es gestionarla. En Windows Phone 8.1 existe una clase llamada StatusBar. En ella encontraremos un método estático, GetForCurrentView, que nos devolverá la instancia de StatusBar para la ventana actual, nuestra aplicación.

Una vez hecho esto, tenemos propiedades como BackgroundColor y ForegroundColor que nos permiten definir el color de fondo y de letra de nuestra barra de sistema, pudiendo adaptarla a los colores usados en la aplicación:

var statusBar = StatusBar.GetForCurrentView();
statusBar.BackgroundColor = (Color)Resources[«BackgroundColor»];
statusBar.ForegroundColor = (Color)Resources[«ForegroundColor»];

De esta forma integramos la propia barra en el diseño de la aplicación y hacemos que de la impresión de que forma parte de la misma:

image

Con solo tres líneas de código integramos la barra perfectamente. Pero además StatusBar incluye una propiedad ProgressIndicator, que nos permite mostrar una barra de progreso indeterminada en la cabecera, así como un mensaje.

También tenemos a nuestra disposición métodos para mostrar y ocultar la barra de estado: ShowAsync y HideAsync.

Y nada más por hoy… No seais desarrolladores perezosos y prestad atención a estos detalles jejeje.

Un saludo y Happy Coding!

[UNIVERSAL] Win2D, Gráficos acelerados por hardware (1 de 2)

spaceship

En Septiembre, Microsoft liberó la primera versión de una librería gráfica para WinRT, llamada Win2D. Se trata de un wrapper sobre Direct2D, compatible con aplicaciones universales. Lo mejor de esto es que nos ofrece una forma muy sencilla de acceder a la potencia de DirectX para dibujar gráficos en dos dimensiones, sin tener que irnos a desarrollar código C++.

El desarrollo de Win2D está en progreso y el equipo va liberando versiones según terminan sprints, por lo que tenemos que tener en cuenta que puede haber fallos, bugs y que no está totalmente implementado Direct2D en este momento. No obstante empieza a ser una herramienta muy interesante y con la que podemos hacer cosas relativamente “resultonas” de forma muy sencilla.

Es importante destacar los tres principios de diseño sobre los que el equipo de Win2D trabaja:

  1. Todo lo que se pueda hacer con Win2D, debe poder hacerse con Direct2D. No se incluirán APis extras en Win2D. De esta forma simplemente se expone Direct2D a más desarrolladores.
  2. Crear una superficie de API que imite a Direct2D de forma que portar código sea más sencillo.
  3. Compartir el código y los progresos con la comunidad de desarrolladores.

Una vez que hemos visto un poco sobre qué es Win2D… ¿Para qué sirve? Podríamos pensar que una libraría 2D en pleno 2014 es algo con una utilidad limitada, pero nada más lejos de la realidad. Precisamente su punto fuerte es que solo es 2D, sencillo y antiguo 2D. Nos quitamos del medio la complejidad del 3D, el consumo de recursos del 3D y nos beneficiamos de una forma de dibujar gráficos bidiménsionales con aceleración por hardware. Podrías usar Win2D para un juego, pero también para hacer un increible fondo de pantalla animado y con aceleración para tu próxima aplicación. Incluso Win2D incluye algunos filtros, muy fáciles de implementar, para imágenes.

Vamos a comenzar por el principio.. Donde está el paquete de NuGet?

Where is my NuGet?

Podemos buscar el paquete de NuGet de Win2D hasta la saciedad y lo cierto es que no lo encontraremos. Actualmente el equipo de Win2D publica todo su código en un repositorio de GitHub, con lo que para usar esta librería tendremos que compilarnos nosotros mismos el paquete. Aunque tranquilos, porque es realmente sencillo de hacer.

Desde Visual Studio, en el panel “Team Explorer”,  vamos a la opción “Connect to team projects” (El icono en forma de enchufe en la parte superior). En la parte inferior encontraremos “Local Git Repositories” y entre otras opciones, una llamada “Clone”. Al presionarla nos pedirá la ruta del repositorio que queremos clonar. En la página del repositorio de GitHub de Win2D podemos encontrar la url para clonarlo, que es la siguiente:

https://github.com/Microsoft/Win2D.git

Simplemente introducimos esta URL en la primera caja de texto y seleccionamos el directorio local donde queramos guardar el repositorio local y presionamos el botón “Clone”:

image

Una vez terminado de clonar, tenemos que abrir una consola de símbolo de sistema de desarrollador (Developer command prompt), navegar al directorio donde hemos descargado el repositorio y ejecutar el archivo build.cmd.

build process

Tendremos que tener un poco de paciencia, porque el proceso tarda un poco en completarse. Al terminar es posible que nos indique que falta NuGet.exe y una línea de comandos a ejecutar para descargarlo. Lo descargamos, vamos al directorio buildnuget y ejecutamos el archivo build-nupkg.cmd y voila! en el directorio bin ya tendremos nuestros dos paquetes NuGet de Win2D.

image

Solo tenemos que irnos ya a las opciones de Visual Studio en el menú “Tools” y en opciones de NuGet agregar la ruta de los paquetes NuGet de Win2D como una nueva fuente de paquetes, de forma que podamos referenciarlos en nuestro proyecto.

Tenemos dos paquetes a nuestra disposición: Win2D y Win2D debug. El segundo permite que depuremos la propia librería Win2D. En este artículo uso la librería normal.

Superficie de dibujo

Una ves que hemos añadido el paquete NuGet a un proyecto universal, podemos empezar a usarlo. La gran baza de Win2D es la sencillez para integrarlo con XAML. Simplemente debemos añadir un nuevo namespace en la página donde queramos usar Win2D apuntando a Microsoft.Graphics.Canvas y añadir a la página el control CanvasControl contenido en ese namespace:

<Page
    x:Class=«Win2DSampleUniversal.MainPage»
    xmlns=«http://schemas.microsoft.com/winfx/2006/xaml/presentation»
    xmlns:x=«http://schemas.microsoft.com/winfx/2006/xaml»
    xmlns:local=«using:Win2DSampleUniversal»
    xmlns:d=«http://schemas.microsoft.com/expression/blend/2008»
    xmlns:mc=«http://schemas.openxmlformats.org/markup-compatibility/2006»
    xmlns:win2d=«using:Microsoft.Graphics.Canvas»
    mc:Ignorable=«d»>

    <Grid>
        <win2d:CanvasControl x:Name=«baseCanvas»/>
    </Grid>
</Page>

Ya tenemos una superficie sobre la que dibujar. Para poder mostrar gráficos, debemos usar una sesión de dibujo, representada por el objeto CanvasDrawingSession. Este objeto se obtiene de la propiedad DrawingSession de CanvasDrawEventArgs, en el evento Draw del CanvasControl, por lo que tendremos que manejarlo para poder dibujar.

Podríamos crear un código como el siguiente para el evento Draw:

private void baseCanvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
    using (var drawSession = args.DrawingSession)
    {
        drawSession.Clear(Colors.Blue);

        Vector2 center = new Vector2()
        {
            X = (float)sender.ActualWidth / 2.0f,
            Y = (float)sender.ActualHeight / 2.0f
        };
        float radius = 150;

        drawSession.FillCircle(center, radius, Colors.LightGreen);
    }
}

Como CanvasDrawingSession implementa IDisposable y además su ciclo de vida es el mismo que el de la ejecución del evento, una vez terminado el evento se pierde la sesión, usamos un bloque using para obtener la instancia de CanvasDrawEventArgs.

Una vez hecho esto, CanvasDrawingSession expone una serie de métodos que nos permiten dibujar diferentes formas como círculos, elipses, texto, rectángulos, líneas o imágenes. También nos permite borrar el contenido de la pantalla para empezar a dibujar de nuevo.

En el código anterior, primero borramos el contenido del canvas estableciendo el fondo azul, a continuación creamos una instancia de la clase Vector2 para definir el centro de un circulo, una variable float para definir el radio y usamos el método FillCircle para dibujarlo, indicando en el último parámetro el color del que queremos rellenarlo. El resultado es el siguiente:

image

Nada espectacular hasta ahora. Además el contenido es estático, el evento Draw solo se invoca una vez al comienzo de la aplicación o si redimensionamos la ventana que contiene la aplicación WinRT. En realidad lo que ocurre es que el método Draw se ejecuta cuando se invalida la superficie del control CanvasControl. Podríamos crear un DispatcherTimer que simplemente la invalidase cada X milisegundos. Si hacemos los cálculos, veremos que para conseguir una tasa de refresco de 60 cuadros por segundo, debemos ejecutar el DispatcherTimer cada 33 milisegundos, más o menos. Por lo que, simplemente en el evento Tick del DispatcherTimer, vamos a invalidar el redibujado. Primero inicializamos el timer en el método OnNavigatedTo de nuestra página:

protected override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    this.random = new Random();
    this.timer = new DispatcherTimer();
    this.timer.Interval = new TimeSpan(0, 0, 0, 0, 30);
    this.timer.Tick += timer_Tick;
    this.timer.Start();
}

En el evento Tick, debemos parar el timer, para evitar que cualquier operación que se alargue más de 30 milisegundos haga que se ejecute varias veces sin llegar a dibujar. A continuación llamamos al método Invalidate de nuestro CanvasControl y se volverá a lanzar el evento Draw:

private void timer_Tick(object sender, object e)
{
    this.timer.Stop();
    baseCanvas.Invalidate();
}

Pero… ¿No vuelves a arrancar el DispatcherTimer al terminar el evento Tick? En realidad… lo deberíamos arrancar de nuevo cuando haya terminado de dibujar, y ya no tengamos nada más que hacer. Esto es tan solo una forma de hacerlo y cada uno debe experimentar con la cadencia de tiempo, con la forma de parar y arrancar el timer… para adaptarlo a lo que necesitemos en cada desarrollo.

Bien, ya podemos invalidar y redibujar cada XX milisegundos pero… ¿De qué nos vale? En el método Draw solo dibujamos un círculo una y otra vez. Pero no se trata de un objeto al que podamos acceder desde fuera… es una simple llamada al método FillCircle. Vamos a complicar un poco más el ejemplo. Como me gusta mucho la ciencia ficción, sobre todo la que trata sobre el espacio… vamos a hacer una vista del espacio con planetas. En primer lugar, ¿Como podemos definir un planeta? Quizás podríamos necesitar saber su posición con respecto a nuestro punto de vista, el radio central para definir su tamaño, el radio de sus anillos (si tiene claro…) El color de su cuerpo y el color de sus anillos. Incluso su aceleración, pues normalmente se trata de cuerpos celestes en movimiento. Así que podríamos desgranarlo en una clase más o menos como esta:

public class Planet
{
    public Vector2 Position { get; set; }

    public float BodyRadius { get; set; }

    public float RingsRadius { get; set; }

    public CanvasRadialGradientBrush BodyColor { get; set; }

    public Color RingColor { get; set; }

    public float Acceleration { get; set; }
}

Podría funcionar… Como podemos ver, el color del cuerpo del planeta se define con una propiedad del tipo CanvasRadialGradientBrush. Este nuevo tipo de brocha, nos permite crear un degradado radial, en ves de uno lineal. Los que hayáis trabajado en WPF, abrazaros y llorad de alegría. Estoy con vosotros.

Para crear un degradado CanvasRadialGradientBrush, al crear la instancia necesitaremos pasarle cierta información:

  • El CanvasControl donde vamos a pintarlo.
  • El color inicial (central)
  • El color final (borde exterior)

Además, una vez hecho esto, necesitaremos indicarle tres propiedades más: Center, RadiusX y RadiusY:

var gradient = new CanvasRadialGradientBrush(baseCanvas, Colors.Yellow, Colors.Orange);
gradient.Center = new Vector2() { X = 10, Y = 10 };
gradient.RadiusX = 40.0f;
gradient.RadiusY = 40.0f;

Es muy importante que el centro sea el mismo que el del objeto donde lo queremos usar. Las coordenadas que indicamos aquí, no son relativas al objeto donde lo apliquemos, son relativas a la pantalla. Es muy importante esto, o podemos darnos cabezazos contra la mesa pensando en porqué no se pinta el degradado correctamente.

Bien, ahora que sabemos como crear el degradado, vamos a crear un método llamado CreatePlanet, al que le pasemos ciertos valores y cree una instancia de un objeto de tipo Planet y lo guarde en una lista:

private void CreatePlanet(CanvasRadialGradientBrush bodyColor, float bodyRadius, float acceleration)
{
    var planet = new Planet();
    Vector2 center = new Vector2()
    {
        X = (float)this.random.Next(0, (int)baseCanvas.ActualWidth),
        Y = (float)this.random.Next(0, (int)baseCanvas.ActualHeight)
    };

    planet.Position = center;
    planet.BodyRadius = bodyRadius;
    planet.BodyColor = bodyColor;
    planet.BodyColor.Center = planet.Position;
    planet.BodyColor.RadiusX = planet.BodyRadius;
    planet.BodyColor.RadiusY = planet.BodyRadius;
    planet.RingColor = planet.BodyColor.Stops.Last().Color;
    planet.RingsRadius = this.random.Next((int)planet.BodyRadius, (int)planet.BodyRadius * 2);
    planet.Acceleration = acceleration;
    this.planets.Add(planet);
}

Este código simplemente rellena las propiedades que hemos indicado anteriormente del planeta. Como podemos ver, a la propiedad BodyColor (CanvasRadialGradientBrush) le pasamos los mismos valores que a la clase Planet para el centro y el radio X e Y.

Pero Yeray, esto es una clase normal y corriente, no es un objeto especial de DirectX super chulo y emocionante… Tranquilos a todo llegaremos. Para lo que nos va servir esta clase es para definir que hay que pintar, de una forma sencilla. Por ejemplo, podemos crear 125 instancias de la clase Planet, muy pequeñas y con un degradado radial de blanco a transparente, con este código:

for (int i = 0; i < 125; i++)
{
    CreatePlanet(new CanvasRadialGradientBrush(baseCanvas, Colors.White, Colors.Transparent),
                 this.random.Next(5, 10), (float)this.random.NextDouble());
}

El radio será de entre 5 y 10 píxeles, y la aceleración un valor aleatorio entre 0 y 1. En nuestro evento Draw, podemos escribir ahora código que recorra la lista de planetas y usar el método FillCircle para dibujar cada uno:

private void baseCanvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
    using (var drawSession = args.DrawingSession)
    {
        drawSession.Clear(Colors.Black);

        foreach (Planet item in this.planets)
        {
            drawSession.FillCircle(item.Position, item.BodyRadius, item.BodyColor);
            drawSession.DrawCircle(item.Position, item.RingsRadius, item.RingColor);
        }
    }

    if (this.timer != null)
        this.timer.Start();
}

Usamos el método FillCiircle para crear el círculo y rellenarlo con el color de cuerpo (El degradado radial que creamos anteriormente) e indicamos la posición y el radio. A continuación usamos el método DrawCircle, para dibujar exactamente igual un círculo, pero en esta ocasión sin relleno, solo dibujando el borde con el color de anillo y la misma posición. Si ejecutamos este código en el simulador de Windows Store, obtendremos algo parecido a esto:

image

Simplemente con un bucle ya hemos dibujado algo parecido a un campo de estrellas… con algo de imaginación al menos. Ahora, vamos a aprovecharnos del DispacherTimer para agregar algo de lógica relacionada con la propiedad Acceleration que hemos incluido en la clase Planet. A ver que os parece:

private void timer_Tick(object sender, object e)
{
    this.timer.Stop();

    foreach (Planet planet in this.planets)
    {
        planet.Position = new Vector2()
        {
            X = planet.Position.X,
            Y = planet.Position.Y < baseCanvas.ActualHeight ? planet.Position.Y + planet.Acceleration : 60
        };
        planet.BodyColor.Center = planet.Position;
    }

    baseCanvas.Invalidate();
}

Con el código anterior, antes de invalidar el CanvasControl, recorremos la lista de planetas y creamos una nueva instancia de Vector2, dejamos la posición X en el mismo punto en el que se encuentra. Con la posición Y, comprobamos si es menor que el area vertical de la pantalla. Si lo es, a la posición Y actual le añadimos la aceleración de la instancia de planeta. Si es mayor, reseteamos la posición a un valor negativo, fuera de la pantalla para que continue avanzando.

Con esto, conseguimos un movimiento vertical descendente de los círculos. Pero además como no todos los planetas tienen la misma aceleración, obtenemos el efecto de profundidad.

Ahora, con el código que hemos creado, podemos seguir creando más planetas, más grandes, con diferentes colores y con más aceleración de forma que obtengamos una mayor sensación de profundidad:

private async Task CreateObjects()
{
    for (int i = 0; i < 125; i++)
    {
        CreatePlanet(new CanvasRadialGradientBrush(baseCanvas, Colors.White, Colors.Transparent),
                     this.random.Next(5, 10), (float)this.random.NextDouble());
    }

    for (int i = 0; i < 7; i++)
    {
        CreatePlanet(new CanvasRadialGradientBrush(baseCanvas, Colors.DarkGoldenrod, Colors.Maroon),
                     this.random.Next(20, 30), (float)this.random.Next(1, 2));
    }

    for (int i = 0; i < 4; i++)
    {
        CreatePlanet(new CanvasRadialGradientBrush(baseCanvas, Colors.Goldenrod, Colors.DarkOrange),
                     this.random.Next(40, 50), (float)this.random.Next(3, 4));
    }

    for (int i = 0; i < 3; i++)
    {
        CreatePlanet(new CanvasRadialGradientBrush(baseCanvas, Colors.Yellow, Colors.Orange),
                     this.random.Next(65, 75), (float)this.random.Next(6, 7));
    }
}

Simplemente añadiendo estos nuevos planetas, nuestro código los dibujará, de una forma parecida a la siguiente:

image

Esto ya parece otra cosa… además, como hemos añadido diferentes aceleraciones en cada capa, desde más lenta a más rápida, obtenemos un mejor efecto de profundidad. Lo se, no es que sea precisamente lo último en tecnología gráfica, pero ilustra lo que os quiero contar jeje.

Ahora que tenemos un campo de estrellas en su lugar, nos falta ¡una nave espacial! y cual que mejor que un X-Wing.

Afortunadamente Win2D incluye métodos y objetos para trabajar con imágenes de forma muy sencilla. Uno de esos objetos es el CanvasBitmap, que nos permite cargar una imagen almacenada en un archivo en el proyecto:

var img = await CanvasBitmap.LoadAsync(baseCanvas, @»AssetsSpaceship.png»);

Usando el método estático LoadAsync y pasándo como parámetro nuestro CanvasControl y la ruta relativa de la imagen en el proyecto, tenemos creado un objeto CanvasBitmap.

Una vez creado el objeto CanvasBitmap, en nuestro método Draw lo podemos mostrar usando el método DrawImage con tres parámetros:

  • el CanvasBitmap a dibujar
  • una instancia de Rect (Windows.Foundation.Rect) para definir la posición (X, Y) y el tamaño de destino.
  • una instancia de Rect  para definir la posición (X, Y) y el tamaño dentro de la imagen original, que queremos pintar.

El que el segundo Rect nos permita definir un fragmento de la imagen, es especialmente útil para trabajar con sprites.  Pero no todo va a ser tan facil. Al igual que con la clase Planet, vamos a definir una clase Spaceship que identifque a nuestra nave espacial. Esta vez es un poco más sencilla: una posición, un desplazamiento negativo y una imagen a dibujar:

public class Spaceship
{
    public Vector2 Position { get; set; }

    public Vector2 NegativeDisplacement { get; set; }

    public CanvasBitmap Image { get; set; }
}

La posición y la imagen, están claras. Pero, ¿Desplazamiento negativo? Bueno, ha sido una idea por darle un poco más de lógica a la demo. ¿Y si al mover la nave en cualquier dirección, aplicamos la misma cantidad de movimiento a las estrellas y planetas, pero en la dirección contraria? Le damos un poco más de interactividad a la lógica.

Primero lo primero, en el mismo método que creaba los planetas, vamos a crear la instancia de la clase Spaceship que vamos a usar:

this.spaceship = new Spaceship();
this.spaceship.Image = await CanvasBitmap.LoadAsync(baseCanvas, @»AssetsSpaceship.png»);
this.spaceship.Position = new Vector2()
{
    X = (float)(baseCanvas.ActualWidth / 2) 60,
    Y = (float)(baseCanvas.ActualHeight / 1.5) 55
};

Simplemente le asignamos el CanvasBitmap y posicionamos la imagen en el centro de la pantalla. Para ello, calculamos el centro y le restamos la mitad del tamaño con el que queremos dibujar la nave.

En nuestro evento Draw, tras dibujar los planetas, vamos a añadir código para dibujar la nave:

if (this.spaceship != null && this.spaceship.Image != null)
{
    drawSession.DrawImage(this.spaceship.Image,
                          new Rect(this.spaceship.Position.X, this.spaceship.Position.Y, 120, 110),
                          new Rect(0, 0, 1952, 1857));
}

Como dijimos anteriormente, el primer Rect indica el destino y el segundo indica el pedazo de la imagen que queremos mostrar. En este caso toda la imagen, que como veréis no me he molestado en disminuir desde una resolución mastodóntica… pensando en hacer algunos juegos de ampliar y reducir la imagen, para dar efecto de elevación… Pero eso para otro tutorial.

image

Ya solo nos queda una cosa: Poder interactuar con la nave moviéndola al pulsar sobre ella. Para ello, vamos a definir la propiedad ManipulationMode del CanvasControl y manejar los eventos: ManipulationStarted, ManipulationDelta y ManipulationCompleted:

<win2d:CanvasControl x:Name=«baseCanvas» VerticalAlignment=«Stretch» HorizontalAlignment=«Stretch»
                     Draw=«baseCanvas_Draw»
                     ManipulationMode=«All»
                     ManipulationStarted=«baseCanvas_ManipulationStarted»
                     ManipulationCompleted=«baseCanvas_ManipulationCompleted»
                     ManipulationDelta=«baseCanvas_ManipulationDelta»
                     Margin=«-50»/>

Como solo queremos manipular la “nave” cuando el usuario pulse sobre ella y la arrastre, en el evento ManipulationStarted, comprobamos si el punto sobre el que empieza la manipulación está contenido en el area de la imagen, definida por la posición + el tamaño. Si está contenida, señalaremos una variable boolean indicando que efectivamente estamos manipulando la nave:

void baseCanvas_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
    if ((e.Position.X >= this.spaceship.Position.X && e.Position.X <= this.spaceship.Position.X + 120) &&
        (e.Position.Y >= this.spaceship.Position.Y && e.Position.Y <= this.spaceship.Position.Y + 110))
    {
        this.movingSpaceship = true;
    }
}

Simplemente comprobamos si la posición X, Y de ManipulationStartedRoutedEventArgs, está dentro del area de la nave. Una vez hecho esto, es en el evento ManipulationDelta donde recibiremos la información mientras el usuario arrastra la imagen, y donde modificaremos su posición:

 

void baseCanvas_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
    if (this.movingSpaceship)
    {
        this.spaceship.Position = new Vector2()
        {
            X = (float)(this.spaceship.Position.X + e.Delta.Translation.X),
            Y = (float)(this.spaceship.Position.Y + e.Delta.Translation.Y)
        };

        this.spaceship.NegativeDisplacement = new Vector2()
        {
            X = (float)e.Delta.Translation.X / 1,
            Y = (float)e.Delta.Translation.Y / 1
        };
    }
}

 

Primero comprobamos si estamos manipulando la nave. A continuación, usamos la propiedad Delta de ManipulationDeltaRoutedEventArgs para obtener la cantidad de traslación realizada desde la última vez que se lanzó el evento. Normalmente el valor es bastante pequeño y relativo al punto donde hayamos tocado. Si desplazamos el puntero/dedo hacia abajo o hacia la derecha, tendremos un valor positivo. Si lo desplazamos hacia arriba o izquierda, un valor negativo. A continuación, cogemos ese mismo valor y obtenemos su inverso (si es 3.2, obtenemos –3.2) y lo guardamos en el Vector2 NegativeDisplacement. Un poco más adelante lo vamos a usar en nuestra lógica.

Por último, el evento ManipulationCompleted nos informa de que el usuario ha dejado de actuar sobre el objeto. En ese momento, podemos establecer la variable boolean a false de nuevo, ya no existe manipulación y reseteamos el NegativeDisplacement a 0, pues ya no hay movimiento:

void baseCanvas_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
    this.movingSpaceship = false;
    this.spaceship.NegativeDisplacement = new Vector2() { X = 0, Y = 0 };
}

Como podemos ver, en ninguno de estos eventos invalidamos el CanvasControl. No lo necesitamos, nuestro DispatcherTimer sigue activo y lo que vamos a hacer es añadir la lógica que tenga en cuenta el NegativeDisplacement a la hora de calcular el movimiento de los objetos:

 

private void timer_Tick(object sender, object e)
{
    this.timer.Stop();

    foreach (Planet planet in this.planets)
    {
        planet.Position = new Vector2()
        {
            X = planet.Position.X < baseCanvas.ActualWidth ?
                planet.Position.X + this.spaceship.NegativeDisplacement.X :
                (float)this.random.Next(0, (int)baseCanvas.ActualWidth),

            Y = planet.Position.Y < baseCanvas.ActualHeight ?
                planet.Position.Y + planet.Acceleration + this.spaceship.NegativeDisplacement.Y :
                60
        };

        planet.BodyColor.Center = planet.Position;
    }

    baseCanvas.Invalidate();
}

 

En primer lugar, tanto en el valor X como Y, comprobamos si estamos dentro de la pantalla. Si estamos dentro, en el eje X le sumamos a la posición actual el valor de X de NegativeDisplacement de la nave. En el eje Y a la posición ya le sumamos el valor de aceleración del propio planeta, para crear la sensación de movimiento. Ahora además le añadimos el valor de Y de NegativeDisplacement. Si estamos fuera de la pantalla o a punto de salir de ella, en el eje X volvemos a calcular un valor aleatorio de X para repintar el planeta en otra parte de la pantalla. En el eje Y simplemente lo pintamos al principio de la pantalla, unos píxeles fuera, para que al animarlo vaya entrando en la misma.

Como podemos ver, no hacemos nada con la nave. En el evento ManipulationDelta ya hemos actualizado el Vector2 Position, con lo que el propio evento Draw la dibujará en la posición adecuada.

Y con esto, si ejecutamos tanto en Windows Store como Phone, podremos mover la nave en todas direcciones y veremos como las estrellas se desplazan en la dirección inversa.

Lo único que nos queda por ver es… como se integra con el resto del XAML. La verdad es que no es ningún misterio, tan solo tenemos que poner el XAML que queramos mostrar, a continuación del CanvasControl y funcionará automáticamente. Por ejemplo, por completar la imitación de un juego de naves, yo he añadido un TextBlock y una ProgressBar que muestran la “energía” restante de la nave:

<Grid>
    <win2d:CanvasControl x:Name=«baseCanvas» VerticalAlignment=«Stretch» HorizontalAlignment=«Stretch»
                         Draw=«baseCanvas_Draw»
                         ManipulationMode=«All»
                         ManipulationStarted=«baseCanvas_ManipulationStarted»
                         ManipulationCompleted=«baseCanvas_ManipulationCompleted»
                         ManipulationDelta=«baseCanvas_ManipulationDelta»
                         Margin=«-50»/>

    <StackPanel VerticalAlignment=«Top» HorizontalAlignment=«Stretch» IsHitTestVisible=«False»>
        <TextBlock Text=«Energy:» FontSize=«24» FontWeight=«Bold» Foreground=«Cyan» Margin=«24,24,0,0»/>
        <ProgressBar x:Name=«EnergyBar» Minimum=«0» Maximum=«100» Value=«100» Background=«Transparent»
                     Margin=«24,12,24,0» Height=«12»>
            <ProgressBar.Foreground>
                <LinearGradientBrush StartPoint=«0,0» EndPoint=«1,0»>
                    <GradientStop Offset=«0» Color=«Red»/>
                    <GradientStop Offset=«.5» Color=«Yellow»/>
                    <GradientStop Offset=«1» Color=«Green»/>
                </LinearGradientBrush>
            </ProgressBar.Foreground>
        </ProgressBar>
    </StackPanel>
</Grid>

Simplemente colocamos el XAML como con cualquier otra página. El resultado final de todo el trabajo que hemos hecho, una app universal, compartiendo todo el código, tanto para Windows como para Windows Phone:

image

Y llegamos al final! Creo que hemos dado un buen repaso por encima de las capacidades básicas de Win2D, como conseguirlo y como poder empezar a usarlo. Quedan más cosas que puede hacer, como aplicar filtros a imágenes, pero eso se va a quedar para el siguiente artículo.

Como siempre, aquí podéis descargaros el ejemplo usado en este artículo. Espero que lo disfrutéis tanto como yo lo he hecho escribiéndolo.

Un saludo y Happy Coding!

 

[AppStudio] Aplicaciones universales a un click de ratón

Hola a todos!

Hace ya casi un año que escribí sobre AppStudio por última vez. Mucho ha llovido desde entonces y AppStudio a cambiado mucho así que, ahora que está de moda en el cine, quiero hacer un “reboot” del artículo original.

En este año que ha pasado, AppStudio ha crecido y cambiado mucho:

Hace un año…

Hace unos minutos…

appstudioOld

appstudioNew

La filosofía sigue siendo la misma, pero el diseño ha evolucionado, al igual que la usabilidad. Ahora puedes generar apps Windows también, incluso cuenta con un “simulador” Windows adicional al de Windows Phone, en el que ver la preview de tus apps. Porque además, AppStudio soporta ahora apps universales:

image

Todo esto y muchas más novedades son lo que me han decidido a reescribir ese artículo original, para repasarlas todas y ver lo que nos puede ofrecer una herramienta que cada vez es más potente.

Vamos a empezar por el principio…

Ahora podemos entrar a AppStudio desde AppStudio.Windows.com. Una vez dentro, tenemos un menú en la parte superior de la pantalla con varias opciones. Las más interesantes ahora: “My projects”, “Start new” y “Sample apps”. Start new nos lleva directamente a crear una nueva app, al igual que el botón “Start new project” que tenemos en la parte superior derecha de la pantalla.

My projects

Esta opción nos lleva a nuestra área personal, donde podremos ver los proyectos que hemos inciado, los hayamos terminado o no, y nos ofrece opciones para manejar cada proyecto:

image

Directamente desde esta pantalla podremos editar un proyecto ya existente con el botón “Edit” de cada uno, generar el código fuente y el paquete de publicación compilado con el botón “Generate” o eliminarlo si no deseamos conservarlo, usando el botón “Delete”.

Sample apps

En el menú “Sample apps” encontraremos dos aplicaciones universales creadas por el equipo de AppStudio:

image

Por cada app, podremos descargarnos su código fuente generado con AppStudio, de forma que tengamos una referencia del tipo de código que genera, como se estructura y como funciona. También vamos a poder ver las aplicaciones en funcionamiento, tanto para Windows Store como para Windows Phone, puesto que ambas aplicaciones de ejemplo están publicadas en las tiendas de la plataforma. Es una forma muy sencilla de ver de que es capaz AppStudio y como lo hace internamente. Si no te las has descargado, hazlo! además he de reconocer que la de Windows App Studio es bastante resultona gráficamente. Kudos para el equipo!

Start new o Start new project

Y llegamos a la parte interesante! con la opción de menú “Start new” o el botón “Start new project” se carga la pantalla de plantillas. Aquí encontraremos tres secciones:

  • Primero las plantillas más usadas: Estas son las plantillas destacadas, que más suelen usarse. Nos permiten crear nuestro proyecto tanto para Windows Phone como para Windows Store.
  • Plantillas web apps: Nos permite crear una aplicación a partir de un sitio web existente. Por ahora solo funciona para Windows Phone.
  • Más plantillas: Son iguales que las primeras que vimos, simplemente más tipos y diseños para que no partas de 0 en tu proyecto.

image

Es importante darnos cuenta que la primera de todas las plantillas, “Empty App” nos permite crear una aplicación totalmente vacía con la que comenzar. Es la que vamos a usar en este artículo, así que vamos a por ella. Pinchamos en “Empty App” y nos mostrará un popup con el preview de la plantilla.

En este caso está totalmente vacía, ya que se trata de una aplicación totalmente en blanco:

image

Presionando el botón “Create” pasamos a la página central de la creación de una aplicación en App Studio:

image

Aquí podemos definir toda nuestra aplicación a golpe de ratón. Lo mejor es que, además, todo lo que vayamos cambiando se irá actualizando en el simulador que tenemos a la izquierda, con lo que podremos ver una preview en tiempo real de como va a quedar nuestra app. Tenemos que tener en cuenta que no es un emulador de Windows Phone, es una simulación, no creo que sea 100% exacta, pero a un 90% seguro que coincide. Pero hay que tenerlo en cuenta.

Lo primero que necesitamos es cambiar el nombre de nuestra aplicación. En este caso yo quiero hacer una app que muestre los vídeos de youtube que tenemos en WPSUG, por lo que la app se llamará WPSUG. Original eh? Podemos cambiar el título escribiendo en la caja de texto que está justo sobre el emulador, donde pone “App Title”.

Ahora vamos a ponerle algo de contenido. Al lado derecho del simulador tenemos 6 cajas punteadas. en cada una de ella podemos añadir una sección. Justo debajo tenemos los tipos de secciones:

  • Rss: El contenido de un RSS que tengamos disponible.
  • Html: Contenido html estático que podemos incluir en la aplicación.
  • Youtube: Una busqueda, un usuario o una lista de reproducción.
  • Flickr: Una busqueda de flickr.
  • Bing: Una busqueda de Bing.
  • Facebook: Una página de facebook.

Debajo de estas, disponemos de dos secciones más, en la categoría avanzada:

  • Menú: Si no tenemos suficiente con las secciones iniciales, limitadas a 6, podemos usar un menú para crear más subsecciones.
  • Colección: Nos permite crear una colección de datos, ya sean estáticos (dentro de la app) o dinámicos (en la nube). En ambos casos puedes definir las columnas que tendrá la colección y el tipo de cada una de ellas.

En nuestro caso, vamos a empezar con una sección HTML, que será la primera que verá el usuario al entrar. En esta sección vamos a explicar un poco que es WPSUG y la finalidad de la aplicación. Al presionar sobre la opción de Html, se abre un popup donde podemos indicar el título de la sección y el contenido HTML:

image

Una vez que estemos contentos con el texto solo tenemos que presionar “Confirm” para terminar de añadir la sección.

A continuación vamos a crear una sección de YouTube, donde mostraremos los videos del usuario “WPSUG”. Este tipo de sección solo nos pide que indiquemos el título, el tipo de busqueda y el término. En nuestro caso, el título será  “Webcasts”, el tipo de busqueda será por usuario y el término será “WPSUG”, como se puede ver en la siguiente pantalla:

image

Presionamos el botón “Confirm”, se añade la nueva sección y ya podremos interactuar con el simulador y ver los datos reflejados. Incluso si pinchamos en el botón verde bajo el teléfono que pone “Windows preview” podremos ver una previsualización, e interactuar con ella, de nuestra app en Tablet.

image

Y ya está, estos eran los datos que queríamos mostrar. Ahora lo que necesitamos es personalizar la aplicación. En primer lugar, podemos presionar en el link “Edit” de cada una de las secciones que hemos creado. Acuerdate de guardar primero con el botón “Save” que tienes en la parte superior derecha, para no perder nada. Al presionar “Edit” veremos la configuración de la página de detalle de cada elemento, en el caso de la sección de tipo YouTube. podremos elegir como se muestra la lista: con o sin imágen, solo título, imágen grande, solo imágenes, bloques con imagen de fondo y texto superpuesto… hay un montón de combinaciones posibles:

image

Cuando tengamos el diseño que queremos, solo tendremos que guardar con el botón “Save” y volver atrás con el botón atrás que hay al lado del nombre de la sección.

Ahora, otra cosa que podemos hacer es cambiar el logotipo por defecto de Windows que se muestra al lado del título. En la parte superior izquierda de la pantalla, al lado de la caja de texto donde escribimos el título de la aplicación, podemos ver el logo por defecto. pinchando sobre él, nos permite seleccionar otro de nuestro equipo, del OneDrive asociado a la cuenta con la que hagamos entrado en AppStudio o de recursos de AppStudio. En mi caso voy a escoger el logo de WPSUG que tengo en mi equipo. La imagen debe cumplir algunos requisitos:

  • Debe tener un alto y ancho mayor de 300 píxeles.
  • Debe ser cuadrada (mismo alto y ancho).

Una vez que escojamos una imágen que cumpla esas dos reglas, se subirá y colocará automáticamente en su posición. Ahora necesito cambiar el Fondo de la app, quiero que sea blanco y el color de la letra, para que la app no sea blanca y negra. Para ello, previo guardado de los cambios, Vamos a pasar de la pestaña “Content” en la que hemos estado a “Themes”:

image

Tenemos varias opciones aquí. En primer lugar podemos escoger el esquema de colores entre tres opciones:

  • Tema oscuro (Dark Style). Es el tema oscuro del dispositivo por defecto.
  • Tema claro (Light Style). Es el tema claro del dispositivo por defecto.
  • Personalizar el tema (Custom Style). Un tema nuevo creado por nosotros.

En el tema personalizado, podemos cambia el color de la barra de aplicación, el color de fondo y el color de letra. Incluso podemos añadir una imagen de fondo a la aplicación si lo deseamos. Una vez que la aplicación quede a nuestro gusto, podemos guardar los cambios y continuar a los Tiles, donde podremos definir los tiles que usaremos en nuestra aplicación. En nuestro caso, vamos a usar el tema oscuro por defecto.

image

Podemos escoger entre tres tipos de tile diferente:

  • Flip template: Un tile que tiene contenido en la parte de atrás y gira para mostrarlo.
  • Cycle template: Un tile al que podemos asociar una colección de imágenes (si tenemos) que se irán mostrando.
  • Iconic template: Un tile con texto y una imagen a mostrar.

En el caso de nuestro ejemplo, vamos a usar el flip template. Nos permite mostrar el logo de WPSUG en varios tamaños y especificar una imagen extra como contenido trasero del tile.

En esta misma pestaña podemos definir también el uso de una splash screen y lock screen. pulsando sobre el botón “Splash & Lock” en la parte de arriba. Tendremos la posibilidad de especificar la imagen que queremos usar en la SplashScreen de Windows Phone y Windows Store y la imagen de lock screen de Windows Phone.

image

Y una vez definido esto último, estamos listos para terminar el proceso en la pestaña de “Publish info”. En esta última pestaña podremos definir varios parámetros de la aplicación:

  • App title: El título de la aplicación.
  • App Description: Una descripción para incluir en la aplicación.
  • Language: Esto es una novedad incluida hace muy poco tiempo. Nos permite indicar el idioma por defecto de la aplicación, de forma que a la hora de certificar no tengamos problemas. Por ejemplo, la aplicaión WPSUG tiene los textos en español. Si selecciono como Language el Inglés, fallará la revisión de la tienda.
  • Include about page: Si lo habilitamos se incluye una página About automáticamente.
  • Enable ad client: Nos permite habilitar la inclusión de publicidad e indicar los datos del PubCenter de Microsoft.
  • Associate app with the Store: permite asociar esta aplicación con la tienda directamente para poder publicarla.
  • Privacy Statement URL: url a la declaración de privacidad de la aplicación. Si no vamos a modificar la aplicación, no es necesario incluir una url propia y AppStudio usará una por defecto.

Ya solo nos queda darle al botón grande y azul que pone “Finish” para poder recoger los frutos de nuestro trabajo. Esto nos llevará a una página donde podremos ver la previsualización de nuestra aplicación tanto en tablet como en teléfono, interactuar con ella y ver si se nos ha olvidado algo:

image

Si todo es correcto, solo tenemos que presionar el botón “Generate” y aparecerá un popup donde podremos indicar que queremos obtener exactamente:

image

En estos momentos, solo podemos generar código/paquetes para Windows Phone 8.1 y Windows 8.1. Podemos escoger si queremos obtener un paquete instalable o uno de publicación y añadir algún comentario. Por úlimo al darle a Generate se cerrará el popup y en la parte de abajo de la pantalla podremos ver un indicador de la generación. Es muy rápido pero no es necesario que esperemos. El sistema nos enviará un email cuando termine, avisándonos de que nuestra aplicación está completa.

image

Es interesante notar que cada generación de una aplicación no pisa la generación anterior. mantenemos como un histórico, de ahí la opción de añadir un comentario. podemos tener varias versiones generadas y ver exactamente que hicimos en cada una apuntándolo en el campo de comentarios.

Y esto es todo por hoy. Espero que os sea muy útil y ayude a que exprimáis todo el jugo a AppStudio.

Un saludo y happy coding!

 

[OFFTOPIC] Starting a new adventure

Hello all!

Today i want to steal some space of my blog to talk about something other than Windows, Windows Phone or XAML. Today i want to talk about myself hehe.

Without getting deep in details, after one year working in a company called Icinetic, yesterday i leave the job. Wait! What? With the actual crisis the world is suffering you are leaving your job? Why? Are you crazy? Well, the why, the crisis, even the job not really matter… The important thing is… i had an awesome year working with such a group of talented people. Share projects, lines of code, adventures and misadventures with a lot of good friends.

In spain there is a popular phrase “lo bueno, si breve, dos veces bueno” (the good things, if brief, twice as good) and another one “no hay mal que cien años dure, ni cuerpo que lo aguante” (there is not bad situations with 100 year durations, and no one can’t hold them) What this two phrases try to explain is: The good things and the bad things aren’t there to stay forever. And i think this was the time to leave in the search of new things… bad or good? i don’t know, and really i don’t care much about that.

So, from today i’m starting a new life as a freelance contractor. Hoping to be able to work with much more intelligent, interesting and tallented people, as i did until today, to try to learn more, do more things and… code a lot of XAML and C# by the way!

Regards and happy coding!

[OFFTOPIC] Empezando una nueva aventura

Hola a todos!

Hoy voy a permitirme robar un poco de espacio para hablar de algo distinto a Windows, Windows Phone, XAML…. Hoy voy a hablar un poco de mi jeje.

Sin entrar mucho en detalles, tras un año en Icinetic ayer fué mi último día. ¿Por qué? Pero si te acabas de mudar a Sevilla! Y que importa realmente… Lo importante es que he pasado un gran año, he tenido el placer de compartir proyecto, aventuras y desventuras con un montón de gente increible y muy muy buena profesional y personalmente, entre ellos Javier Suarez, un gran compañero. He tenido la oportunidad de trabajar en un proyecto muy interesante, con mucha difusión y tras terminar en ese proyecto he podido pasar los últimos 8 meses con un proyecto Windows Phone XAML muy chulo, con bluetooth LE de por medio, mucho (ojo, pero muuuuuucho) trabajo de animaciones y efectos en XAML y bastante grande para lo que suele ser una app móvil.

Pero como dice el dicho: “lo bueno, si breve, dos veces bueno” o era “no hay mal que mil años dure, ni cuerpo que lo aguante”? Bueno, ya me entendéis.

Ahora me lanzo a una nueva aventura, la de hacerme autónomo… o “Contractor” como se dice en la lengua de Shakespeare. Con mucha ilusión, con muchas ideas. Sin un duro, pero con muchas ganas de desarrollar más apps, de escribir más, y de compartir más conocimiento.

¿Qué podría ir mal? Un gran compañero me enseño que no hay nada que no se arregle con un “Malo será…”.

Un saludo a todos y Happy Coding!!

[XAML] Peek view en Visual Studio 14 CTP4

Hola a todos!

Desde ayer está disponible la CTP 4 de Visual Studio 14. Trae un monton de novedades, pero hoy quiero hablaros de una, que por su simplicidad, es una auténtica maravilla para los que pasamos muchas horas trabajando con XAML.

Hablo de Peek definition. En español se podría traducir como “ver definición”. No debemos confundirlo con el Go to definition que ya teníamos disponible en Visual Studio 2013. Aunque su función es parecida, la forma en que se ha implementado es totalmente diferente.

En Visual Studio 2013 ya teníamos Peek Definition para el código C#/VB.NET/C++, pero no para XAML. Este nos permitía ver en línea, sin cambiar de archivo ni posición, la definición del miembro sobre el que la usemos:

image 

Pues por fín se han decidido a implementarlo en XAML también. Funciona con controles de usuario y recursos, de forma que, por ejemplo, podemos ver la definición de una plantilla, sin cambiar de pantalla y editarla mientras vemos los resultados en el contexto real donde se usa:

image

Si trabajas con muchas páginas XAML distintas, controles de usuario y estilos en varios diccionarios de recursos, podrás apreciar la ventaja de “Peek definition” sobre “Go to definition”. Como extra, decir que funciona en cualquier “sabor” de XAML: Tanto proyectos Windows Phone y Windows Store, como WPF, que ya se merecía algo de cariño.

Un saludo y Happy Coding!!

[MATERIALES] Madrid Mobility Day 2014

Hola a todos!

El pasado 30 de septiembre, junto a Javier Suarez Ruíz, Alejandro Campos y Luis Guerrero tuve el placer de participar en el Madrid Mobility Day, organizado en las oficinas de Microsoft Ibérica.

Para empezar a caldear el ambiente, la primera charla que hice fue una breve introducción al desarrollo de aplicaciones universales para Windows 8.1 y Windows Phone 8.1. He subido la presentación a SlideShare para que podáis acceder a ella fácilmente:

También pude compartir charla con Javier Suarez Ruiz en una charla sobre Xamarin.Forms. Javi publicará la presentación y los ejemplos en su blog.

Como siempre, ha sido un placer poder estar con toda la gente de DX de Microsoft Ibérica. Incluso aprovechamos para sacarnos una foto Javi y yo en una de las cafeterías:

photo

Un saludo y Happy Coding!