Etiquetado: Windows 8.1

Inhabilitar la transición de entrada para algunos controles del Grid XAML

Las animaciones de entrada, salida, añadir elemento, seleccionar, ordenar, etc., que trae el SDK de la Tienda Windows aportan un toque de calidad a nuestras apps. Las páginas que creamos desde Visual Studio incorporan algunas transiciones por defecto, aunque en algunas ocasiones nos interesará inhabilitarlas.

Eliminar las transiciones de entrada de un Grid es sencillo, sólo tendremos que borrar la colección de transiciones que ya nos habrá añadido el SDK en la página y, por si acaso, asegurarnos que no queda ninguna:

Sigue leyendo

Anuncios

Migrando el tres en raya: Azure SDK 2.4, SignalR 2.1.1, Windows 8.1

El año pasado escribí una serie de tres artículos donde explicaba cómo crear un juego de tres en raya para jugar online en tiempo real, desde el navegador y también desde una app Windows 8. Como la tecnología avanza muy rápido, al cabo de un mes de escribir el artículo el código ya no compilaba con los últimos SDK. Así que, ahora que estoy de vacaciones me he dedicado a procrastinar y a actualizar alguno de mis proyectos personales para no tener que hacer lo que me había propuesto :). Aquí os dejo los pasos a realizar para migrar, aunque podéis descargar directamente el nuevo paquete aquí.

Como primer paso, conviene tener Visual Studio 2013.3 instalado y actualizar el SDK de Windows Azure al último disponible (2.4 por ahora).
Sigue leyendo

Tareas de fondo en aplicaciones universales

Hace unos días @johnnyjosep me preguntó cómo podía hacer en una aplicación universal para descargar nuevo contenido cada cierto tiempo sin tener la aplicación abierta.

Sí, os debo la serie de artículos sobre el caché de HTTP, aunque si os fijáis bien en este hay un pequeño ejemplo de uso de la cabecera If-Modified-Since.

La forma más eficiente de notificar a la aplicación que algo remoto ha cambiado, es utilizar un sistema de notificaciones push, estilo Azure Notification Hubs, pero como esa opción tiene un coste, también tenemos otra posibilidad más económica (para nosotros): crear una Background Task con un temporizador, que compruebe desde el cliente si el archivo ha cambiado.

Sigue leyendo

Consumir SOAP desde WinJS

soap
No todos los servicios proporcionan una API Restful para acceder a ella de forma sencilla desde nuestras Apps, así que es posible que tengamos que pelearnos con servicios SOAP, que son más pesados y complicados, pero a la vez nos permiten trabajar operaciones y conjuntos de datos más complejos.
Consumir SOAP desde JavaScript no es complicado, sólo que es tedioso. Hoy vamos a ver dos maneras que podremos utilizar en nuestras aplicaciones de la tienda Windows.

Descarga el código de ejemplo de Codeplex

Consumir SOAP con xhr

WinJS.xhr es un wrapper del objeto XMLHttpRequest para facilitarnos las llamadas asíncronas, así que utilizar SOAP desde xhr va a ser tan sencillo o complicado como con el XMLHttpRequest original.

Para el siguiente código vamos a utilizar uno de los ejemplos de servicios web SOAP que hay en http://www.webservicex.net/, en concreto el servicio ChangeLengthUnit que realiza conversión de unidades.

En una aplicación de la tienda Windows creamos una página y en el tag body ponemos un cuadro de texto, un botón y un div como estos:

Introduce los centímetros: <input type="number" id="mmValue" value="0.0"/>
<button id="xhrTest">Conexión con WinJS.xhr</button>
<div id="result"></div>

En default.js, añadimos el código que sigue tras la línea donde se llama a WinJS.UI.ProcessAll(), creando el código XML que enviaremos al servicio SOAP para llamar a la acción SOAP ChangeLengthUnit. Podéis ver un ejemplo en la página de descripción de la operación, necesita tres parámetros: el valor numérico, las unidades origen y las unidades destino. Para el ejemplo convertiremos de centímetros a pulgadas:

var data = '<?xml version="1.0" encoding="utf-8"?>' +
            '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + 
            ' xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
            ' xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
            '  <soap:Body>' +
            '    <ChangeLengthUnit xmlns="http://www.webserviceX.NET/">' +
            '      <LengthValue>'+mmValue.value+'</LengthValue>' +
            '      <fromLengthUnit>Centimeters</fromLengthUnit>' +
            '      <toLengthUnit>Inches</toLengthUnit>' +
            '    </ChangeLengthUnit>' +
            '  </soap:Body>' +
            '</soap:Envelope>';

var options = {
    url: "http://www.webservicex.net/length.asmx",
    type: "post",
    headers: {
        "Content-Type": "text/xml; charset=utf-8",
        "SOAPAction": "http://www.webserviceX.NET/ChangeLengthUnit"
    },
    data: data
};

Fijaos que he configurado la llamada xhr para llamar por POST y he incluido dos cabeceras específicas para la llamada, el Content-Type, que debe ser xml y la SOAPAction, que debe indicar a qué servicio estamos llamando. Ahora llamamos a la función xhr con las opciones que hemos configurado y esperamos la respuesta, que vuelve a ser un XML del que podremos extraer la información.

WinJS.xhr(options)
.done(
    function (request) {
        var doc = request.responseXML.documentElement;
        var output = doc.getElementsByTagName("ChangeLengthUnitResult");

        result.innerHTML = "La longitud es "+ window.toStaticHTML(output[0].textContent)+" pulgadas";
        result.style.backgroundColor = "#00A000";
    },
    function (error) {
        result.innerHTML = "Status: " + error.status + " : " + error.statusText;
        result.style.backgroundColor = "#FF0000";
    },
    function (progress) {
        result.innerText = "Ready state is " + progress.readyState;
        result.style.backgroundColor = "#0000A0";
    });

Todo esto para un servicio web bastante sencillo. Si empezamos a añadir tipos de datos complejos la cosa se complica.
Como estamos en Windows 8 / 8.1, tenemos una gran ventaja, podemos realizar la conexión con el servicio web mediante C#, que es capaz de leer la descripción WSDL del servicio y generar un proxy con las clases tipificadas, para luego utilizar esa clase desde JavaScript a través de las proyecciones WinMD.

Puente SOAP con C#

Como ya comenté en un artículo anterior, podemos utilizar librerías escritas en C# o C++ desde nuestros proyectos JavaScript, sólo tenemos que crear una librería tipo Windows Runtime Component y respetar unas normas básicas. Una vez creada la librería y referenciada desde nuestro proyecto JavaScript, utilizar servicios SOAP es muy fácil, basta con pulsar botón derecho sobre el proyecto C# y añadir una referencia al servicio. El Visual Studio nos creará las clases y llamadas que necesitamos para comunicarnos con el mismo.

Add WebService

Con la referencia al servicio ya tenemos todo lo necesario, sólo nos falta crear un método dentro de una clase C# para publicarlo y que se pueda utilizar desde JavaScript. Como las llamadas al servicio son asíncronas creamos un método que devuelve un tipo IAsyncOperation; podemos convertir la Task del cliente SOAP directamente a este interfaz con el método de extension AsAsyncOperation:

public sealed class LengthUnits
{
    LengthUnitService.lengthUnitSoapClient client;
    public LengthUnits()
    {
        client = new LengthUnitService.lengthUnitSoapClient();
    }
    public IAsyncOperation<double> GetAsync(double number,
        LengthUnitService.Lengths from, LengthUnitService.Lengths to)
    {
        return client.ChangeLengthUnitAsync(number, from, to).AsAsyncOperation();
    }
}

Et voilà, ya lo podemos utilizar desde JavaScript sin ensuciarnos las manos parseando XML:

csTest.onclick = function (e) {
    var cl = new SoapClient.LengthUnits();
    cl.getAsync(mmValue.value, SoapClient.LengthUnitService.Lengths.millimeters, SoapClient.LengthUnitService.Lengths.inches).done(
        function (c) {
            result.innerHTML = "La longitud es " + c + " pulgadas";
            result.style.backgroundColor = "#00a000";
        },
        function (err) {
            result.innerHTML = window.toStaticHTML(err);
            result.style.backgroundColor = "#FF0000";
        },
        function (progress) {
            result.innerHTML = window.toStaticHTML("percent: " + progress + "%");
            result.style.backgroundColor = "#0000a0";
        }
        )
}

Progreso

Un momento, ¿ahora no está llegando nunca a la función de progreso? Pues no, porque para tener progreso necesitamos utilizar el interfaz IAsyncOperationWithProgress. Nos llevará un poco más de trabajo, la clase AsyncInfo y las palabras async y await nos lo van a poner fácil:

    public Windows.Foundation.IAsyncOperationWithProgress<double, string> GetWithProgressAsync(double number,
        ServiceReference1.Lengths from, ServiceReference1.Lengths to)
    {
        return AsyncInfo.Run(async delegate(CancellationToken cancellationToken,
            IProgress<string> progress)
        {
            double result;
            progress.Report(client.State.ToString());
            await client.OpenAsync();
            progress.Report(client.State.ToString());
            result = await client.ChangeLengthUnitAsync(number, from, to);
            progress.Report(client.State.ToString());
            await client.CloseAsync();
            progress.Report(client.State.ToString());
            return result;
        });
    }

Seguro que hay alguna manera más, cuando la encuentre os la contaré!

Descarga el código de ejemplo de Codeplex

Mejorar el rendimiento del canvas HTML5 desde C#

IMG_4102
Desarrollar aplicaciones para la Tienda Windows en JavaScript tiene sus ventajas e inconvenientes. Aunque en la mayoría de los casos no lo vamos a notar, cuando tratamos grandes cantidades de datos y tenemos que realizar modificaciones sobre estas, lo más probable es que se penalice el rendimiento.

Estoy desarrollando la nueva versión de Fingerpaint y quiero añadir una herramienta de relleno de color.
No es complicado de hacer, hay bastantes algoritmos bien optimizados y el context tiene los métodos getImageData y putImageData que nos dan el array de datos para modificar el contenido del canvas a nivel de pixel:

width = context.canvas.width;
height = context.canvas.height;
imageData = context.getImageData(0, 0, width, height);
data = imageData.data;
for(var i=0;i<data.length;i++){
  //tratar los píxeles de data uno por uno
}

El problema de hacer esto en JavaScript es que el lenguaje no está optimizado para este tipo de operaciones e intentar hacer un relleno un poco inteligente (estilo cubo de pintura) va a tardar mucho más de lo que estamos acostumbrados con cualquier aplicación de dibujo moderna.

Componentes Windows Runtime (WinRT)

Por suerte, mi aplicación FingerPaint es para Windows 8 (en realidad 8.1) y puedo usar librerías desarrolladas en otros lenguajes, como C# o C++, que me permitirán realizar estas operaciones mucho más rápido.
Es muy fácil tener código en otros lenguajes dentro de una aplicación JavaScript de la tienda Windows, sólo tenemos que añadir una nueva librería de tipo Windows Runtime Component en el lenguaje que prefiramos. Con este tipo de proyectos estamos indicando que es una librería que puede utilizarse desde cualquier otro lenguaje.

Lo que estamos haciendo es crear una librería con WinMD, el mismo sistema de metadatos que las librerías de sistema en Windows Runtime. En esta imagen creamos un nuevo componente en C#:
Windows Runtime Component

A continuación tenéis el ejemplo de código. Está dividido en dos métodos:

public sealed class Fill
{
   private byte[] _data;

   public void BeginFill(int x, int y, int width, int height, 
       [ReadOnlyArray] byte[] bitmap, string hexColor, byte tolerance)
   {
     _data=bitmap;
     //
     //aquí irá mi código de flood fill que modificará los datos
     //dentro de _data, pero al ser ReadOnly no volverán a JavaScript
     //modificados.
     //
   }

   public void EndFill([WriteOnlyArray] byte[] target)
   {
     _data.CopyTo(target,0);
   }
}

Si os fijáis, la clase es sealed y los parámetros array están decorados con los atributos Read/WriteOnlyArray. Estas dos características son requisito indispensable para que nuestra clase y métodos se puedan utilizar desde cualquier otro lenguaje.

Añadiremos el proyecto que hemos creado en las referencias de nuestra aplicación JavaScript y desde el código JavaScript las llamadas se verán así, como si fueran llamadas a cualquier otra librería JavaScript, pero tú y yo sabemos que está hecho en C#:

width = context.canvas.width;
height = context.canvas.height;
imageData = context.getImageData(0, 0, width, height);
data = imageData.data;
//creamos un objeto Fill
var helper=new ImageHelpers.Fill();
//calculamos el fill
helper.beginFill(x,y,width,height,data,"#00ff00",50);
//escribimos el resultado en imageData
helper.endFill(data);
//escribimos el resultado en el canvas
context.putImageData(imageData, 0, 0);

Para poder modificar el canvas necesitamos que nuestro método externo modifique directamente el array obtenido con getImageData, pero cuando trabajamos con componentes de WinRT y queremos pasar un array sólo podemos hacerlo en uno de estos dos modos:

  • ReadOnlyArray: trabajamos sobre una copia del array y cualquier modificación no se traspasará entre los contextos, ni siquiera si devolvemos el array como valor de retorno
  • WriteOnlyArray: podemos escribir sobre el array original… pero este siempre viene vacío, no veremos la información original

Adicionalmente, canvas no nos permite introducir un array de datos dentro de imageData que no haya sido creado por el propio canvas, así que si utilizáramos un parámetro de retorno tendríamos que copiar uno por uno los bytes del array, con lo que perderíamos el rendimiento que ganamos por hacerlo en C#.
Entonces, y por eso el artículo de hoy, tenemos que realizar la operación en dos pasos: en el primero obtenemos los datos originales y en el segundo escribimos sobre la memoria del canvas desde C#. Estas operaciones son bastante rápidas y casi no penalizan el rendimiento (entre 1 y 3 milisegundos en un i5), así que nos queda bastante margen para modificar el contenido del canvas y mejorar considerablemente la velocidad del fill con nuestro método C#.

Depuración híbrida

Para poder depurar nuestro código C# dentro de una aplicación JavaScript tendremos que indicar en las propiedades del proyecto qué tipo de lenguaje queremos depurar, a alguien se le olvidó poner Managed+Script así que por ahora sólo podremos depurar en uno o en otro, si hubiéramos elegido C++ como lenguaje no tendríamos este problema, pero tendríamos otros :P.
Script or Managed debug

Happy finger painting!

Usar SQLite en Apps Windows 8.1

Cuando necesitamos guardar datos estructurados que acostumbramos a tener en una base de datos, tenemos unas cuantas alternativas, entre ellas usar SQLite, una librería que incluye un motor SQL sobre un archivo del disco, con relaciones, índices, transacciones e incluso consultas estilo LINQ.

SQLite es una librería nativa, pero gracias a Visual Studio, NuGet y a los desarrolladores de la librería, será muy fácil integrarlo en nuestra aplicación.

Cómo agregar SQLite en nuestra App

El primer paso será descargar el paquete de librerías preparado para Windows Runtime 8.1 de la página de descargas de SQLite.
Debemos buscar el paquete para WinRT 8.1 en la sección Precompiled Binaries for Windows Runtime. Es un archivo .VSIX que podremos instalar como paquete en el Visual Studio.
La versión que he utilizado para este artículo es la 3.8.2 que podéis descargar aquí.

SQLite VSIX

Una vez instalado, en nuestro proyecto C# o VB.Net de la Tienda Windows, añadiremos la referencia a la librería SQLite. Podemos encontrarla en Windows>Extensions:

SQLite Windows Runtime Libraries

Este paquete nos incluirá la librería SQLite 3, pero esta librería es nativa y no se puede utilizar directamente desde C# o VB.Net. Para empezar a usarla necesitaremos una clase que encapsule las llamadas. Por suerte ya está hecha, basta descargar sqlite-net a través de NuGet:

sqlite-net NuGet Package

Este paquete instala dos archivos c# que nos hacen de puente con la librería y nos permiten ejecutar consultas contra una base de datos sqlite:
sqlite.cs

Ejemplo de uso

Ya sólo nos queda crear una base de datos, empezar a grabar y a consultar datos. Como es un archivo local, tendremos que copiar una .db existente o bien crear una dentro del sistema de archivos de la aplicación (nos sirven tanto la carpeta temporal como la local). Aquí vemos un pequeño ejemplo de definición, creación e inserción.

public class Person{
    [SQLite.PrimaryKey,SQLite.AutoIncrement]
    public int Id { get; set; }

    [SQLite.MaxLength(50)]
    public string Name { get; set; }

    [SQLite.MaxLength(50)]
    public string Surname { get; set; }

}

private async void createDB()
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var conn = new SQLite.SQLiteAsyncConnection(
        System.IO.Path.Combine( folder.Path,"test.db"));
    
    await conn.CreateTableAsync<Person>();            
}

private async Task insertPersonAsync(string name, string surname)
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var conn = new SQLite.SQLiteAsyncConnection(
        System.IO.Path.Combine(folder.Path, "test.db"))
    await conn.InsertAsync(new Person { Name = name, Surname = surname });
}

Compartir sin contratos en Windows 8

Thumbs Up - Like
Ya sabemos que en Windows 8 y 8.1 existe una forma muy buena de compartir contenido: a través del contrato de compartir. En las presentaciones y documentación que hay sobre el tema descubrimos la enorme ventaja que supone el contrato de compartir: sólo lo programas una vez, luego es el usuario que decide con qué aplicación quiere compartir. Es decir, si el usuario tiene Facebook, Twitter, Linkedin, WordPress o lo que sea que utilice para compartir su vida, tendrá probablemente una/varias/todas esas Apps instaladas en su equipo y es la propia aplicación de la red social preferida del usuario que sabe cómo compartir el contenido en esa red.
Gracias a esto, nosotros programamos la función compartir que proporciona lo que nuestra aplicación sabe compartir: una imagen, un enlace web, un texto, un archivo, etc., o todo a la vez, y cuando esa información llega hasta la App que elija el usuario es esa App la encargada de publicar el contenido en la red social de turno.

Compartir en Windows 8

Todo eso se hace con la clase DataTransferManager que nos permitirá compartir nuestro contenido en unas pocas líneas, tal como se reproduce en el ejemplo:

 function setupShare() {
        var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
        dataTransferManager.addEventListener("datarequested", function (e) {
            var request = e.request;
            request.data.properties.title = "Share Demonstration";
            request.data.setText("Hello World!");
        });
    }

Vale, pero aún así yo quiero compartir en una red en concreto desde mi App

Si todavía no te ha convencido el sistema, o no te fías de que el usuario se haya descargado la App, nada te impide utilizar las diferentes APIs de cada plataforma para compartir contenido (o poner Me Gusta, Favoritos, etc.). Normalmente necesitas:

  1. Darte de alta como desarrollador de la plataforma
  2. Obtener una clave de API
  3. Realizar una conexión para obtener un token
  4. Mantener es token hasta que caduque y solicitar uno nuevo cada vez que ocurra esto.
  5. Finalmente llamar a una url que te deja compartir

O puede… ser aún peor, necesitas que el usuario haga un login en la plataforma y te de permiso para hacer un montón de cosas con su cuenta, en su nombre. No hay problema, también Windows 8/8.1 te permite simplificar eso con el WebAuthenticationBroker del que os hablaré otro día.

Y ¿no hay una forma más fácil?

Por suerte, muchas plataformas te permiten lanzar una url que va a su web, el usuario hace el login directamente en la plataforma y luego acepta la publicación, sin necesidad de clave de api ni de más programación por nuestra parte que lanzar una simple url. Aquí van dos ejemplos en C#, aunque en JavaScript no serán muy diferentes:

  • Con Facebook no se puede hacer directamente un like, tendrías que poner un control WebBrowser e incrustar el botón de Like, pero en cambio puedes hacer muy fácilmente un share:
    string url = "https://jmservera.com/2013/10/07/optimizar-el-canvas-de-apps-windows-en-html5javascript/";
    
    string shareUrl = string.Format("https://www.facebook.com/sharer/sharer.php?u={0}",
        System.Net.WebUtility.UrlEncode(url));
    
    await Windows.System.Launcher.LaunchUriAsync(
        new Uri(shareUrl));
    
  • En twitter podrías hacer algo así al pulsar el botón:
    string url = "https://jmservera.com/2013/10/07/optimizar-el-canvas-de-apps-windows-en-html5javascript/";
    string texto = "Cómo optimizar código JS en Canvas";
    
    string shareUrl = string.Format("http://www.twitter.com/intent/tweet?url={0}&text={1}",
        System.Net.WebUtility.UrlEncode(url),
        System.Net.WebUtility.UrlEncode(texto));
    
    await Windows.System.Launcher.LaunchUriAsync(
        new Uri(shareUrl));
    

¿Y si quiero poner un Like?

Facebook te da un código para incrustar en tu página, para que puedas poner un Like de la misma, pero eso no funcionará directamente en una App de la Tienda Windows, ni siquiera en las Apps HTML5/JavaScript. Ya sabréis que las Apps no permiten ejecutar código externo directamente… a no ser que las metamos en un iFrame o en el nuevo WebView, en este ejemplo en versión HTML5:

<x-ms-webview id="webview"
                  height="35" width="300"
                  src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fjmservera.com&amp;width=300&amp;height=35&amp;colorscheme=dark&amp;layout=standard&amp;action=like&amp;show_faces=false&amp;send=false&amp;"
                  style="border:none; overflow:hidden; width:300px; height:35px;background-color:rgb(29,29,29)">
    </x-ms-webview>

Espero que os sirvan.
Happy sharing!

Cambios en la publicación de Juegos para Windows Store

Si habéis publicado algún juego en la Tienda Windows sabréis que hace falta un archivo GDF para la clasificación por edades en los distintos países donde se publica la aplicación. También habréis sufrido la locura de tener que crear una dll de C++ para embeber el archivo GDF aunque vuestro juego esté hecho en C# o JavaScript.

Bien, pues hace muy poco han cambiado las reglas y el archivo GDF se sube a la tienda por separado, ya no hace falta generar la dll sino que al subir el paquete del juego a la tienda tenemos un lugar en el portal donde cargar el GDF y los certificados pertinentes:

Image

Cuidado con las instrucciones en la web en castellano pues todavía no se han traducido. En el msdn inglés ya lo tenéis: http://msdn.microsoft.com/en-us/library/windows/apps/hh465153.aspx

Las nuevas características de búsqueda en Windows 8.1

Windows 8.1 Preview presenta grandes novedades con respecto a la versión actual. Una que me ha chocado mucho es el nuevo sistema de búsqueda. Tiene cosas muy buenas y otras no tanto, pero tengamos en cuenta que todavía es una versión preliminar y puede cambiar mucho hasta el lanzamiento.

found dog

Desde el punto de vista del desarrollador, en la versión anterior era muy fácil y directo integrarse con el contrato de búsqueda; permitía a los usuarios buscar dentro de nuestra aplicación aunque ésta estuviera cerrada, haciendo que nuestra aplicación estuviera siempre presente.

Cookbook search old

A pesar de los esfuerzos de Microsoft por explicar las bondades del sistema de búsqueda, en el mundo real™ los usuarios no acababan de entender el sistema o simplemente no encontraban el botón de búsqueda dentro de la aplicación y pensaban que la aplicación no podía buscar. En las aplicaciones no había ninguna evidencia de que pudieran buscar y al final poníamos un botón dentro de la aplicación que abría el charm de búsqueda. Además, cuando empiezas a tener muchas aplicaciones que implementan el contrato de búsqueda la lista de posibilidades se vuelve demasiado larga para ser manejable.

Por este motivo y porque la funcionalidad del búsqueda dentro de cualquier aplicación sólo la utilizaba el 7% de los usuarios, el equipo de Windows decidió cambiar el sistema de búsqueda para mejorarlo y hacerlo más eficiente.

¿Dónde están las Apps?

El nuevo charm de búsqueda ya no muestra una lista de aplicaciones disponibles. En su lugar nos permite buscar dentro de todo el sistema, integrando los resultados del contenido indexado en nuestro equipo con otros datos devueltos por Bing, Xbox Music y otros.
Las aplicaciones antiguas que implementan el contrato de búsqueda de Windows 8 no perderán esa funcionalidad; podremos seleccionar la aplicación para que nos devuelva la página de resultados, pero sólo nos aparecerá esta opción cuando sea la aplicación activa y no estará seleccionada por defecto, pero esto último se puede cambiar.

cookbok search 0

Una vez seleccionada nuestra aplicación en lugar del “Search everywhere” podremos seguir usándola como antes, aunque no tendremos la posibilidad de cambiar a otra aplicación:

Cookbok search

¿Cómo haremos para que se pueda buscar dentro de nuestra aplicación desde cualquier sitio?

Está algo más escondido para nosotros, pero en realidad será mejor que antes, pues parece ser que podremos ofrecer resultados dentro de la búsqueda general. Todavía no está disponible en la preview que podemos probar ahora, si bien en esta charla del Build podemos ver cómo será.
Además, la funcionalidad “Search everywhere” permite encontrar nuestra aplicación si el usuario la tiene instalada y, como me suele ocurrir mucho, no recuerda exactamente el nombre:

search everywhere

¿Y cómo implemento ahora las búsquedas?

Podemos optar por seguir haciéndolo como antes o bien utilizar los nuevos controles y capacidades de búsqueda.

Charm de búsqueda

En el caso de que queramos seguir con la implementación antigua, lo recomendación es que pongamos un botón en la zona superior derecha que abra el charm de búsqueda, así nuestros usuarios sabrán que nuestra aplicación puede buscar. Al ejecutar el siguiente código, el charm estará enfocado en nuestra aplicación en lugar de en el “Search everywhere”, e incluso podemos poner un término de búsqueda por defecto:

Windows.ApplicationModel.Search.SearchPane.getForCurrentView().show("buscar...");

Todavía tenemos una opción mejor, hacer que el cuadro de búsqueda aparezca al empezar a escribir, tal como hacen las aplicaciones de calidad y la pantalla de inicio. El código para añadir esta funcionalidad es muy sencillo:

Windows.ApplicationModel.Search.SearchPane.getForCurrentView().showOnKeyboardInput = true;

El control SearchBox

Si queremos ajustarnos a las nuevas recomendaciones de búsqueda, el equipo de Windows nos lo ha puesto muy fácil. Tenemos un nuevo control de búsqueda que tiene eventos muy parecidos al contrato de búsqueda, pero es un control visual que podemos poner en nuestro UI, tanto para XAML como para HTML:

<div class="pagesearch" data-win-control="WinJS.UI.SearchBox"></div>

Una vez en nuestra página, podremos usar el código que utilizábamos antes en nuestro contrato de búsqueda que utilizaba la consulta:

var search = element.querySelector(".pagesearch").winControl;
search.onquerysubmitted = function (e) {
    console.log(e.detail.queryText);
};

Sólo que ahora tendremos que navegar nosotros a la página de resultados.

También podemos añadir sugerencias de búsqueda:

search.onsuggestionsrequested = function (e) {
    var query = e.detail.queryText.toLowerCase();
    var suggestionCollection = e.detail.searchSuggestionCollection;
    var suggestionList = ["Almería","Albacete", "Alicante"];
    for (var i = 0, len = suggestionList.length; i < len; i++) {
        if (suggestionList[i].substr(0, query.length).toLowerCase() === query) {
            suggestionCollection.appendQuerySuggestion(suggestionList[i]);
        }
    }
};

Activar el foco del control al usar el teclado en la aplicación:

search.winControl.focusOnKeyboardInput = true;

Y ofrecer resultados recomendados con imágenes y separadores para identificarlos del resto, lo que dará un toque de calidad a nuestra aplicación:

search.onsuggestionsrequested = function (e) {

    var queryText = e.detail.queryText,
        query = queryText.toLowerCase(),
        suggestionCollection = e.detail.searchSuggestionCollection;
    var suggestionList = [{
        name: "Almería",
        thumb: "http://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Bandera_de_Almer%C3%ADa.svg/200px-Bandera_de_Almer%C3%ADa.svg.png"
    },
        {
            name: "Albacete",
            thumb: "http://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Albacete-Bandera.svg/200px-Albacete-Bandera.svg.png"
        },
        {
            name: "Alicante",
            thumb: "http://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Flag_of_Alicante.svg/200px-Flag_of_Alicante.svg.png"
        }];

    for (var i = 0, len = suggestionList.length; i < len; i++) {
        if (suggestionList[i].name.substr(0, query.length).toLowerCase() === query) {
            if (suggestionCollection.size <2) {
                var reference = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(
                    new Windows.Foundation.Uri(suggestionList[i].thumb));
                suggestionCollection.appendResultSuggestion(suggestionList[i].name,
                    "El mejor resultado!", "id1", reference, "mejor resultado");
            }
            else {
                if (suggestionCollection.size == 2)
                    suggestionCollection.appendSearchSeparator("Más sugerencias");
                suggestionCollection.appendQuerySuggestion(suggestionList[i].name);
            }
        }
    }
};

sugerencias

Por lo que podemos ver es un control muy completo, que además cachea las últimas búsquedas para ofrecerlas también como sugerencia.

El indexador de Windows

Además del nuevo control de búsqueda, podemos añadir y consultar elementos con el indexador de Windows mediante la clase ContentIndexer. Antes de que os frotéis las manos como hice yo, leed la explicación que aparece en la documentación:

Content is separated per app package, per user, and a package can’t query another package’s data.

Podemos utilizar la potencia del indexador de Windows, pero sólo para y por nuestra aplicación, los datos que añadamos al índice son privados para nuestra aplicación y usuario.

Dicho esto,

  • La clase ContentIndexer nos permite añadir elementos al índice de Windows, de manera que podremos aprovechar la potencia del indexador de Windows dentro de nuestra aplicación. Por ahora el contenido que añadamos al índice será sólo para nuestra aplicación, el resto de las aplicaciones no pueden verlo.
  • También podemos añadir contenido indexado utilizando carpetas locales y creando ficheros appcontent-ms.
  • Con las clases del espacio de nombres Windows.Storage.Search podremos realizar búsquedas dentro del contenido indexado utilizando Advanced Query Syntax, pero sólo del contenido propio.
  • Todavía no sabemos cómo podremos hacer para aparecer dentro de las búsquedas generales de Windows Search.

Conclusiones

Tras estudiar el uso que hacían de la búsqueda los usuarios de las aplicaciones de la tienda Windows, el equipo de Windows se dio cuenta que sistema necesitaba un cambio. Este cambio ha tenido algunas consecuencias pero tenemos la promesa de que va a mejorar. Mientras tanto, ya podemos aprovechar la potencia que nos proporcionan los nuevos controles y funcionalidades del sistema de búsqueda.

Referencias

Abrir en nueva ventana en Windows 8.1

Abrir en nueva ventana en Windows 8.1

La versión de Tienda Windows de IE 11 te da la opción de abrir enlaces en otra pestaña y también en una ventana nueva. Gracias al sistema de división flexible de pantallas de Windows 8.1, con la segunda opción se abre la nueva ventana al lado de la anterior, permitiendo así la navegación en múltiples paginas simultáneamente.

Este ha sido mi primer comentario realizado íntegramente con una tableta Surface RT, pantallazo incluído y utilizando únicamente el teclado en pantalla. La verdad es que me está resultando más cómodo de lo que me pensaba. Saludos desde la playa 😎.