Formularios interactivos dentro de un ListView

En algunas aplicaciones de la Tienda Windows vemos una colección de elementos dentro de un ListView en las que uno de ellos contiene un formulario con el que podemos interactuar, por ejemplo en esta aplicación, que no sólo tiene uno sino dos:
booking

Vamos a ver cómo podemos crear un interfaz parecido en nuestras aplicaciones HTML5/JavaScript de la Tienda Windows.

Plantilla condicional

Podéis ver en el artículo sobre diseño de listas heterogéneas cómo escribir diferentes plantillas y cargarlas condicionalmente. En nuestro caso vamos a crear una plantilla adicional para el primer elemento de la lista e insertaremos un elemento select, sólo a efectos de demostrar la técnica, sirve con cualquier cosa que tengamos dentro del elemento.

Empezamos con la plantilla de Aplicación de Cuadrícula (Grid App), que ya tiene datos y un control ListView para poder realizar la demostración. Una vez creada, en la página groupedItems.html copiamos la plantilla itemTemplate justo debajo y la modificamos un poco, añadiendo la etiqueta select con tres opciones:

<div class="interactiveitemtemplate itemtemplate" data-win-control="WinJS.Binding.Template">
    <div class="item">
        <img class=" item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
        <div class="item-overlay">
            <!--<h4 class="item-title" data-win-bind="textContent: title"></h4>-->
            <select>
                <option>Opt 1</option>
                <option>Opt 2</option>
                <option>Opt 3</option>
            </select>
        </div>
    </div>
</div>

Tras añadir la plantilla adicional, podemos modificar la función ready del código de groupedItems.js para que se ejecute la nueva plantilla al crear el primer elemento del ListView (tal como comento en el artículo que he citado antes) y la plantilla general para el resto, sustituyendo el itemTemplate por una función que ejecuta una plantilla u otra según el índice:

var standardTemplate = element.querySelector(".itemtemplate").winControl;
var interactiveTemplate = element.querySelector(".interactiveitemtemplate").winControl;
listView.itemTemplate = function (itemPromise) {
    return itemPromise.then(function (item) {
        var template;
        if (item.index==0) {
            template = interactiveTemplate;
        }
        else {
            template = standardTemplate;
        }
        return template.render(item.data);
    });
};

El modo interactivo: win-interactive

Si ejecutamos el código anterior veremos el primer elemento con la plantilla que hemos aplicado pero no podremos interactuar con el selector. El comportamiento por defecto de los elementos de un ListView es que podamos pulsar sobre el elemento para realizar una navegación. Como nosotros queremos interactuar directamente con el contenido del elemento necesitamos desactivar ese comportamiento por defecto.

En este caso el truco es muy sencillo: basta con añadir la clase win-interactive al elemento que queremos activar, WinJS se encargará del resto de la magia:

<div class="interactiveitemtemplate itemtemplate" data-win-control="WinJS.Binding.Template">
    <div class="item win-interactive">
...

A partir de ahora todo el recuadro se volverá interactivo y dejará de responder como un elemento de ListView. Si queremos que el elemento contenedor siga actuando a la pulsación podemos poner la clase win-interactive sólo en el elemento select … o en el que queramos nosotros.

En otro post ya os contaré más trucos para crear efectos parecidos, pero sin un control ListView.

About these ads

Cómo usar bien la fórmula calc en CSS3 y la importancia del espacio

Hoy un truco rápido que me ha hecho perder un buen rato en una app de Windows 8, suerte que se me ha ocurrido buscar en MSDN.

A veces vamos a necesitar que un elemento nos ocupe un porcentaje de la página/aplicación, pero dejando libre un cierto margen de pixels.

Hay muchas formas de hacerlo, pero en el caso que tenía hoy necesitaba asignar un margen de 20px y un tamaño del elemento al 100% para que cubriera todo el espacio restante del contenedor.
Las propiedades de estilo margin y padding no se llevan demasiado bien con asignaciones a height y width en formato de porcentaje, el 100% calculado seguirá siendo el total del espacio del contenedor sin el margen. Así que necesitamos realizar un cálculo entre el valor del porcentaje y la cantidad de pixels que estamos utilizando en los margenes. Para esto no hace falta código JavaScript, pues tenemos la función calc que permite hacer exactamente eso en CSS, pero tenemos que ir con mucho cuidado al escribirla, busca las diferencias:

Código erróneo:

.homepage section[role=main] {
    margin-top:20px;
    height:calc(100%-20px);
}

Código correcto:

.homepage section[role=main] {
    margin-top:20px;
    height:calc(100% - 20px);
}

La diferencia es sutil pero importante: entre el 100%, 20px y el símbolo de restar, en un caso no hay espacios y en el código que funciona sí. Es muy fácil olvidar los espacios y si no los ponemos no pasará nada, nada de nada, pues, tal como dicta la norma, un atributo css inválido es ignorado.

Creación de nuestros propios snippets en dos o tres clicks

Los snippets de Visual Studio son una herramienta estupenda que nos ahorra muchísimo tiempo a la hora de escribir código. Los snippets que vienen predefinidos en VS.Net nos permiten escribir mucho código escribiendo las dos primeras letras y a golpe de tecla Tab crear propiedades, bucles y un sinfín de llamadas.

Lo que pasa con este tipo de herramientas es que tarde o temprano necesitas más, por eso te puedes crear tus propios snippets con un poco de XML, pero lo que quiero yo es que sea rápido de hacer, para esto tenemos una extensión de Visual Studio llamada Snippet Designer. Os cuento en 2 pasos cómo instalarla y en 4 más cómo crear y utilizar el snippet, no más de 5 minutos para ahorrarnos tecleos y ahorrarle un montón de dinero a nuestro jefe en horas de picar código :)

  1. Lo primero, instalar el Snippet Designer. Abrimos Tools>Extensions and Updates:

    createSnippet01

    Y buscamos online el Snippet Designer

    createSnippet02

  2. Una vez instalado ya podemos seleccionar el trozo de código del que queremos crear el snippet y seleccionar la opción Export as Snippet:

    createSnippet03

  3. Entonces se abrirá un editor que nos permitirá definir el snippet de forma visual: qué claves será posible cambiar desde el snippet (botón derecho>Make Replacement), cómo se llamará ese elemento, qué valor tiene por defecto, de que tipo es, etc…

    createSnippet05

    Y una ventana de propiedades donde podremos definir las características del snippet, una muy útil es el Shortcut:

    createSnippet07

  4. Una vez lo tenemos guardado (se guarda en My Snippets), reniciamos el VS.Net y ya podemos utilizar nuestro nuevo snippet, nos bastará escribir el shortcut:

    createSnippet08

    Y pulsar el tabulador para que el snippet se expanda y nos permita escribir en él:

    createSnippet09

Que lo disfrutéis!

Vuelve el Megathon


Hoy ha empezado la formación online para el Megathon 2013 que tendrá lugar los días 12, 13 y 14 de abril en múltiples ciudades. El año pasado un servidor junto con Netsaimada y el MICTT y con la colaboración de edib, UIB, Microsoft DPE, SD Assessors, IBLabs, el ParcBit que nos dejaba el auditorio e IBRed que nos puso la WiMAX hicimos dos hackathones de desarrollo de aplicaciones de Windows 8. Hubo una participación enorme y un nivel muy bueno, más teniendo en cuenta que Windows 8 era una completa novedad y teníamos que explicarlo todo desde cero y en muy poco tiempo.
megathon

Si os gustó el Megathon del año pasado, este año será mucho mejor. Además de los mentores que estaremos durante el evento para resolveros vuestras dudas, DPE ha organizado toda una serie de sesiones de formación online programadas hasta el día antes del Megathon. Encontraréis sesiones de Windows Phone y de Windows 8 con los mejores tutores que hay en España, para que estéis completamente preparados y poder darlo todo para crear la mejor aplicación de Windows Phone o Windows 8 en un fin de semana.

Tendremos entre nosotros a expertos en XAML/C#, C++ e incluso alguno de HTML5/JavaScript :) para las dudas que puedan salir y alguna demo de aparatos voladores controlados por un Windows 8. Si queréis ver todo esto y mucho más, nos vemos en el Megathon. Apuntaos ya antes de que se acaben las plazas!

Binding a imágenes de carpetas especiales en Windows 8

Descarga el código de este artículo en Codeplex

Hoy para cambiar un poco haré un ejemplo en XAML/C#, pero seguimos con aplicaciones de la tienda Windows.
Os explicaré un truco que se puede realizar de dos formas: la primera, la forma automática, utilizando una clase del sistema que ya nos hace casi todo el trabajo y la segunda, una forma manual de realizarlo, que dejaremos para más adelante.

Las carpetas especiales

En las aplicaciones de la Tienda Windows podemos acceder directamente a algunas carpetas especiales del sistema siempre que declaremos en el manifiesto que queremos acceder y el usuario lo sepa:
pictureslibrary
Una vez hemos establecido el requisito en el manifiesto, ya podemos utilizar la clase Windows.Storage.KnownFolders para listar los elementos de la carpeta de imágenes:

var queryOptions = new Windows.Storage.Search.QueryOptions();
queryOptions.FolderDepth = FolderDepth.Deep;
var query = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);

Nota: si intentáis esto en otra carpeta, como por ejemplo “Mis Documentos”, no funcionará, pues también hay que declarar que tipos de archivo vamos a abrir y sólo podremos abrir esos.

Listar todos los archivos dentro de la carpeta

Para obtener una lista de todos los archivos podemos llamar al método GetFilesAsync() y luego podremos trabajar sobre la misma. Si vamos a representar esa lista en el interfaz de usuario, WinRT nos ofrece una forma mucho mejor: podemos obtener una lista virtualizada, esto es, una lista con el mismo número de elementos que contiene la carpeta y las características de esos elementos se van obteniendo de forma asíncrona. De esta forma obtendremos los primeros resultados mucho más rápido y veremos en nuestra colección en pantalla el tamaño completo de la lista, aunque todavía no se hayan obtenido todos los resultados.

public class DataSource:INotifyPropertyChanged
{
    object _items;
    public object Items
    {
        get { return _items; }
        private set
        {
            _items = value;
            onPropertyChanged("Items");
        }
    }

    public DataSource()
    {
        try
        {
            var queryOptions = new Windows.Storage.Search.QueryOptions();
            queryOptions.FolderDepth = FolderDepth.Deep;
            var query = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
            var fileInformationFactory = new FileInformationFactory(query,
                Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
                150, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true);
            Items = fileInformationFactory.GetVirtualizedFilesVector();
        }
        catch (Exception ex)
        {
            Items = new object[] {new{ Name = ex.Message} };
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void onPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Si observamos el código, veremos que la clase FileInformationFactory nos permite generar también una vista previa de cada uno de los archivos. El método no es asíncrono, esto quiere decir que nos devolverá muy rápidamente un resultado: la lista virtualizada llena de elementos vacíos, que luego se irán rellenando en segundo plano.

Ahora sólo nos hace falta crear un contenedor GridView para mostrar los archivos:

<Page
    x:Class="ImageBinding.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ImageBinding"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:localData="using:ImageBinding.DataModel">
    <Page.Resources>
        <localData:DataSource x:Key="mainDataSource"></localData:DataSource>
        <CollectionViewSource Source="{Binding Path=Items, Source={StaticResource mainDataSource}}"
                              x:Key="mainView"></CollectionViewSource>
        <DataTemplate x:Key="DataTemplate">
            <Grid Width="150">
                <TextBlock Text="{Binding Name}" TextTrimming="WordEllipsis" ></TextBlock>
            </Grid>
        </DataTemplate>
    </Page.Resources>
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" 
        DataContext="{StaticResource mainDataSource}">
        <GridView
            Margin="0 90 0 0" ItemsSource="{Binding Source={StaticResource mainView }}" 
                  ItemTemplate="{StaticResource DataTemplate}" SelectionMode="None">
        </GridView>
    </Grid>
</Page>

Carga de imágenes

Una vez tenemos la lista de archivos vamos a mostrar las imágenes que os prometí al principio del artículo. La colección que nos proporciona FileInformationFactory.GetVirtualizedFilesVector() contiene elementos que cumplen con el interfaz IStorageItemInformation. Estos elementos tienen una propiedad Thumbnail a la que podremos enlazarnos para visualizar la imagen, pero no podremos hacerlo directamente pues el tipo StorageItemThumbnail en realidad es un tipo Stream y no una imagen, así que necesitaremos un Converter para poder utilizar la imagen dentro del interfaz de usuario:

public class StreamToBitmapConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value != null)
        {
            var stream = (IRandomAccessStream)value;
            var img = new BitmapImage();
            img.SetSource(stream);
            return img;
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

A través del Converter ya podremos hacer el enlace correctamente:

<Page.Resources>
    <localData:DataSource x:Key="mainDataSource"></localData:DataSource>
    <CollectionViewSource Source="{Binding Path=Items, Source={StaticResource mainDataSource}}"
                          x:Key="mainView"></CollectionViewSource>
    <localConverters:StreamToBitmapConverter x:Key="bitmapConverter"/>
    <DataTemplate x:Key="DataTemplate">
        <Grid Width="150">
            <Grid.RowDefinitions>
                <RowDefinition Height="70"/>
                <RowDefinition Height="32"/>    
            </Grid.RowDefinitions>
            <Image Grid.RowSpan="2" Source="{Binding Thumbnail, 
                Converter={StaticResource bitmapConverter}}"></Image>
            <Border Grid.Row="1" Background="#80000000" Padding="5">
                <TextBlock Text="{Binding Name}" VerticalAlignment="Center" 
                           TextTrimming="WordEllipsis" />
            </Border>
        </Grid>
    </DataTemplate>
</Page.Resources>

Y aquí tenemos el resultado:
picturesCollection
En el próximo artículo os contaré cómo hacer lo mismo pero programando nosotros la carga asíncrona de imágenes bajo demanda.

Descarga el código de este artículo en Codeplex

Filtros REST $filter con texto no ASCII desde JavaScript

Los servicios OData son muy cómodos pues se pueden consumir desde casi cualquier parte y tecnología. Consumir estos datos desde cualquier aplicación HTML/JavaScript, incluso desde las aplicaciones de Windows 8, es muy sencillo pues nos basta con utilizar las llamadas HTTP de toda la vida para interactuar con el servicio.

Desde las consultas REST podemos filtrar utilizando el parámetro $filter en la url, así podemos consultar el catálogo de películas de Netflix y filtrar para que sólo nos dé las series:
http://odata.netflix.com/v2/Catalog/Titles?$filter=Type eq ‘Series’

En la lengua de Shakespeare no tendremos ningún problema, pero si queremos filtrar con caracteres no ASCII tendremos comportamientos que no serán los que nos esperábamos. Por ejemplo, filtramos por el nombre José dentro del mismo servicio:
http://odata.netflix.com/v2/Catalog/Titles?$filter=substringof(‘José’,Name)
Y obtenemos resultados como este, donde el nombre José no aparece:
netflix_nonasciifilter
De hecho, dependiendo del servicio y desde dónde lo llamemos obtendremos resultados diferentes, pues la interpretación de los caracteres que recibe se puede hacer de varias formas.

La solución está en codificar el filtro dentro de la url, para codificar el texto la función adecuada en JavaScript es encodeURIComponent, pues la función escape produce también resultados no deseados. Podéis probar los ejemplos en jsfiddle, aunque al ser JSONP también produce efectos diferentes según como codifiquemos; podemos comparar el jsfiddle con el resultado de consultar directamente las urls:

En Windows 8 tendremos el mismo problema y lo resolveremos igual, utilizando la función encodeURIComponent:

var query = "José";
var encoded = encodeURIComponent(query);
WinJS.xhr({
    url: "http://odata.netflix.com/v2/Catalog/Titles?$filter=substringof('" +
        encoded + "',Name)&$format=json"
}).then(function (xhr) {
    var result = JSON.parse(xhr.responseText);
    var results = result.d.results;
    // a partir de aquí utilizamos los datos
    var body=document.querySelector("body");
    results.forEach(function (r) {
        var div=document.createElement("div");
        div.textContent=r.Name;
        body.appendChild(div);
    });
});

Antes de acabar, recordad que para enviar la consulta también deberíamos comprobar si el texto a buscar contiene el carácter de comilla simple (‘), pues deberemos sustituirlo por la misma comilla escrita dos veces seguidas (”).
Solucionado con un poco de regex:

query=query.replace(/'/g, "''")

Micrófono en HP TouchSmart con Windows 8

En la oficina tenemos unos cuantos HP TouchSmart 300, que a pesar de no estar pensados para Windows 8 y tener sólo dos puntos de contacto, dan bastante juego a la hora de probar las aplicaciones en modo táctil, tanto de Windows 8 como de Windows Phone.

De algunos dispositivos no es fácil encontrar los drivers y hay que descargarlos de aquí y de allá, el que me ha costado más ha sido el driver del micrófono, así que aquí va una pequeña ayudita para los que os hayáis encontrado con el mismo problema:

  1. Hay que descargar el driver actualizado de SoundMAX, pues el Windows Update no lo reconoce directamente. Para ello:
  2. El driver viene dentro de un archivo .cab, así que lo tendremos que descomprimir para poder instalarlo en Windows 8. Con el mismo explorador de Windows podemos abrirlo, o con alguna aplicación para descomprimir como WinRAR.
  3. Una vez descomprimido, buscamos el adihdaud.info, pulsamos con botón derecho->instalar

    instalar soundmax

  4. Para comprobar que tenemos instalado el “Microphone Array”, pulsamos Windows+X y abrimos el administrador de dispositivos:

    microfono instalado

Y listos, ya podemos utilizar nuestras aplicaciones de audio favoritas (y también Skype y cualquier otra app en condiciones).

La funcionalidad deshacer y repetir en JavaScript para Windows 8

Undo button
Los participantes de un hackathon tienen que poner todo su empeño y mucho más para conseguir en pocos días crear una aplicación brillante; eso les obliga a buscar soluciones para problemas que no se habían planteado nunca antes. Durante los hackathones, megathones y demás formaciones que he ido dando sobre Windows 8 suelen surgir dudas más profundas que en un curso estándar sobre algún lenguaje o tecnología concretos.

Una de las cuestiones más recurrentes suele ser la funcionalidad deshacer en una aplicación, ya sea una calculadora o un programa de dibujo. En este artículo voy a intentar solucionaros esa duda con un ejemplo de código.

Descarga el código fuente de esta aplicación desde codeplex

Patrones de diseño

Como no me gusta reinventar la rueda vamos a hacer uso de uno de los dos patrones de diseño del GoF que se suelen utilizar en estos casos, a saber:

  • Memento: nos permite almacenar el estado de un objeto, respetando la encapsulación del mismo, para poder recuperarlo después.
  • Command: encapsula una petición como un objeto, con el método y los parámetros que va a utilizar sobre el receptor de la petición. Esto nos permite crear una cola de solicitudes y permitir el retroceso de operaciones (siempre que la operación lo permita).

Dependiendo del caso utilizaremos un método u otro. Por ejemplo, en el caso de un programa de dibujo sobre lienzo el Memento nos puede resultar más fácil de implementar, pues cada vez que se dibuje un elemento podemos guardar una foto del mismo, o una porción para optimizar memoria. En el caso de un procesador de textos el patrón Command seguramente será mucho más óptimo, pero cada comando tendrá que implementar su propia función deshacer.

Para hacer un ejercicio sencillo vamos a basarnos en el patrón memento para desarrollar un ejemplo.

El patrón Memento en JavaScript

En el patrón Memento utilizamos tres objetos:

  • Memento: es el almacén del estado interno del objeto, guarda la información necesaria para recuperar el estado del objeto a un momento concreto
  • Originator: crea el Memento que contiene la instantánea de su estado interno y sabe restaurar ese estado usando el Memento
  • Caretaker: gestiona la lista de Mementos, pero no utiliza nunca la información del Memento directamente.

Para demostrar el uso del patrón utilizaremos una aplicación de pintado en un canvas ya utilizada en este blog para tratar los eventos táctiles.

Empezaremos por definir un espacio de nombres para evitarnos problemas en JavaScript y definiremos el objeto básico Memento:

(function mementoDefinition(global) {
    global.CanvasState = global.CanvasState || {};

    ///Memento
    CanvasState.memento = function (state) {
        this._state = state;
    };
    Object.defineProperty(CanvasState.memento.prototype, "state", {
        get: function () { return this._state; }
    });
    ///...

Podréis observar que es extremadamente sencillo: un constructor con el estado y una propiedad para leer el estado. No es modificable ni tiene ninguna funcionalidad, sólo almacena información.

El siguiente paso es crear el objeto Caretaker, el que se encarga de almacenar la lista de objetos Memento; necesitaremos un Array para almacenar los estados de deshacer y otro para los estados de rehacer.

En esta implementación ponemos un parámetro de límite, para evitar llenar toda la memoria con las operaciones de undo. Para gestionar los estados tenemos las funciones addMemento, que nos permite ir añadiendo estados a la lista y las funciones getUndoMemento y getRedoMemento para recuperar los estados, dependiendo de si queremos deshacer una operación o volver a hacerla desde el historial.

///Caretaker constructor
CanvasState.caretaker = function (maxLevels) {
    this._undoStates = [];
    this._redoStates = [];
    this._maxLevels = maxLevels;
};

CanvasState.caretaker.prototype.addMemento = function (memento) {
    this._undoStates.push(memento);
    this._redoStates = [];
    if (this._undoStates.length > this._maxLevels) {
        this._undoStates.splice(0, 1);
    }
};

CanvasState.caretaker.prototype._canUndo = function () {
    return this._undoStates.length > 1;
}
CanvasState.caretaker.prototype._canRedo = function () {
    return this._redoStates.length > 0;
}

CanvasState.caretaker.prototype.getUndoMemento = function () {
    if (this._canUndo()) {
        var state = this._undoStates.pop();
        this._redoStates.push(state);
        return this._undoStates[this._undoStates.length - 1];
    }
    else
        throw "Undo not allowed, states array empty";
};
CanvasState.caretaker.prototype.getRedoMemento = function () {
    if (this._canRedo()) {
        var state = this._redoStates.pop();
        this._undoStates.push(state);
        return state;
    }
    else
        throw "Redo not allowed, states array empty";
}

Object.defineProperty(CanvasState.caretaker.prototype, "canUndo", {
    get: function () { return this._canUndo(); }
});
Object.defineProperty(CanvasState.caretaker.prototype, "canRedo", {
    get: function () { return this._canRedo(); }
});

Por ahora, con estos dos objetos no hemos realizado ninguna operación con el objeto canvas, sólo tenemos el objeto que almacena el estado y el que se encarga de mantenerlo en memoria.

El tercer y último objeto es el Originator, donde ocurren todas las operaciones. Este objeto sabe cómo crear un Memento y cómo recuperar el estado a partir del mismo. En el ejemplo vamos a utilizar el objeto canvas para recuperar una imagen y la guardaremos dentro de un Memento que podremos utilizar más adelante para restaurar el estado.

    ///Originator
    CanvasState.originator = function (canvas) {
        this._canvas = canvas;
        this._ctx = canvas.getContext("2d");
    };
    CanvasState.originator.prototype.saveToMemento = function (x, y, w, h) {
        if (x === undefined)
            x = 0;
        if (y === undefined)
            y = 0;
        if (w === undefined)
            w = this._canvas.width;
        if (h === undefined)
            h = this._canvas.height;

        var img = this._ctx.getImageData(x, y, w, h);
        return new CanvasState.memento({ image: img, x: x, y: y, w: w, h: h });
    };
    CanvasState.originator.prototype.restoreFromMemento = function (memento) {
        this._ctx.putImageData(memento.state.image, memento.state.x, memento.state.y);
    };

})(this);

Y así cerramos la función auto-ejecutable que define todos nuestros objetos. Todo el código anterior lo podemos meter en un archivo llamado memento.js y así lo podremos reutilizar fácilmente.

Como podréis observar, en todo el código el único objeto que sabe algo del canvas es el objeto originator.

Aplicación del patrón a un caso real

Ahora que tenemos todos los objetos preparados, vamos a utilizarlos en la aplicación que hicimos en un post anterior sobre los eventos táctiles. Para demostrar los eventos pintábamos sobre un canvas y ahora vamos a ir guardando el estado para poder recuperarlo más tarde.

Para empezar, en default.js añadimos unas variables que vamos a utilizar para la nueva funcionalidad, justo debajo de las declaraciones del canvas y el ctx:

var originator, caretaker;

Seguimos en default.js, tras los eventos táctiles dentro de app.onactivated, crearemos los objetos originator y un caretaker de 20 niveles de deshacer:

// creamos el originator sobre nuestro canvas
// y un caretaker de 20 niveles
originator = new CanvasState.originator(canvas);
caretaker = new CanvasState.caretaker(20);

//lo primero que haremos será añadir el canvas vacío como memento.
caretaker.addMemento(originator.saveToMemento());

La función startDrawing quedará exactamente igual, donde vamos a guardar el estado es al levantar el dedo de la pantalla, en la función stopDrawing, que quedará así:

function stopDrawing(e) {
    doDraw = false;
    caretaker.addMemento(originator.saveToMemento(0, 0, canvas.width, canvas.height));
}

Ahora guardamos los cambios cada vez que levantamos el dedo de la pantalla. Nos falta poder recuperarlos. Para ello vamos a añadir una barra de botones en la página default.html justo debajo del canvas:

<div id="appBar" data-win-control="WinJS.UI.AppBar" data-win-options="{sticky:true}">
    <button data-win-control="WinJS.UI.AppBarCommand" 
        data-win-options="{id:'cmdUndo',label:'Undo',icon:'undo',section:'global'}">
    </button>
    <button data-win-control="WinJS.UI.AppBarCommand" 
        data-win-options="{id:'cmdRedo',label:'Redo',icon:'redo',section:'global'}">
    </button>
</div>

La marcamos como sticky para que no desaparezca mientras pintamos y nos resulte más cómoda de utilizar. Añadimos dos manejadores de evento en default.js, después de las líneas que crean el originator y el caretaker:

document.getElementById('cmdUndo').winControl.addEventListener("click", function () {
   if (caretaker.canUndo)
      originator.restoreFromMemento(caretaker.getUndoMemento());
});

document.getElementById('cmdRedo').winControl.addEventListener("click", function () {
   if (caretaker.canRedo)
      originator.restoreFromMemento(caretaker.getRedoMemento());
});

Vuestro turno

Está claro que a este código le queda mucho margen de mejora, así que hoy os voy a poner deberes:

  • Intentad mejorar el dibujado cuando hay múltiples puntos táctiles simultáneos
  • Para mejorar el rendimiento y la gestión de memoria de la app, deberíamos almacenar sólo la zona que se ha modificado del canvas en lugar de guardarlo todo. ¿Os atrevéis?

Descarga el código fuente de esta aplicación desde codeplex

Error 401 al actualizar datos en WCF Data Services sobre Azure

401 Unauthorized
Cuando necesitamos acceder a los datos de una manera sencilla desde todas nuestras aplicaciones y posibilitar su uso desde casi cualquier tecnología o dispositivo, una apuesta bastante segura es crear un servicio REST de acceso a los datos.

Para crear un servicio REST de forma muy rápida, Microsoft nos proporciona una herramienta casi mágica llamada WCF Data Services. Esta librería, junto con una base de datos en SQL Azure, nos creará automáticamente un servicio REST en base a un modelo de entidades (Entity Framework, Linq to SQL, etc..), que luego podremos configurar para establecer permisos con unas pocas líneas.

Podéis encontrar multitud de artículos sobre el tema, como por ejemplo este tutorial rápido en MSDN.

Artículo recomendado: Guidance for OData in Windows Azure

Cuando nos funcione el servicio en local, nuestra alma geek nos pedirá probar en Azure incluso antes de configurar la autentificación. Si ese es el caso y la aplicación, además de leer y crear datos, también actualiza y borra, recibiremos un error 401 al realizar estas últimas operaciones.
Esto es así porque las operaciones de lectura y creación se realizan con los verbos GET y POST de HTTP, pero la actualización y borrado de datos se realizan mediante los verbos PUT y DELETE, que en IIS están deshabilitados en el caso de la autentificación anónima.
Sin que sirva de precedente, aquí tenéis un truco rápido para evitar esta situación, pero recordad que debe ser algo temporal y debéis evitar que se puedan guardar, modificar y borrar datos con una conexión anónima!
Es suficiente deshabilitar cualquier tipo de autentificación para evitar el problema. En el web.config de nuestra aplicación añadimos esta entrada en la sección system.web:

<system.web>
   <authentication mode="None" />
</system.web>

Y ya nos debería funcionar, pero recordad habilitar la autentificación de nuevo y establecer mecanismos de seguridad.

Si vuestras necesidades de datos son sencillas y queréis que sea lo más automático y seguro sin esfuerzo, también tenéis los Servicios móviles de Azure

Protección infantil en Windows 8 dentro de un dominio

El término BYOD (Bring Your Own Device) está cada vez más de moda. Estos últimos años se ha generalizado debido a la gran cantidad de dispositivos que entran en nuestros hogares y que acabamos llevando al trabajo, aunque el termino también se refiere a los trabajadores que prefieren comprarse ellos mismos el portátil en lugar de utilizar uno proporcionado por la empresa, probablemente más antiguo y menos potente.

Para variar un poco, yo he hecho lo contrario y tengo un dispositivo del trabajo en casa. Lo que se llama ahora BYOD inverso, aunque existe hace muchos más años que el BYOD.

Windows 8 con múltiples usuarios

Una de las ventajas que tiene Windows 8 sobre otros sistemas que funcionan sobre tabletas es su capacidad de mantener múltiples cuentas de usuario. Esta funcionalidad nos permite por una parte mejorar la seguridad y por otra evitar tener en nuestra cuenta todas las aplicaciones de Hello Kitty™, Pocoyo™, Disney™ y similares para que jueguen nuestros niños; una pantalla táctil en casa es demasiado atractiva como para poder acapararla el 100% del tiempo :)

Windows 8 Multi-usuario

Protección Infantil integrada en Windows 8

Tener cuentas separadas para nuestros hijos tiene otras ventajas añadidas: podremos filtrar qué webs pueden abrir, qué aplicaciones pueden utilizar, qué apps pueden descargar del Windows Store, tanto de forma manual mediante listas blancas y negras como de forma automatizada con filtros por edades. En Windows 8 ya está integrada la protección infantil como parte del sistema operativo, mientras que en versiones anteriores la podemos descargar de forma gratuita con el paquete Windows Live Essentials.
Para utilizar la protección infantil en Windows 8 basta marcar la casilla de seguridad cuando estemos añadiendo una cuenta de usuario, indicando que el nuevo usuario necesita el filtro de protección infantil:

Agregar un usuario infantil en Windows 8

No voy a extenderme demasiado sobre el tema, pues tenéis toda la información y el paso a paso en el apartado sobre seguridad familiar de la web de Windows.

Protección infantil en dominios de Windows

Si utilizáis equipos que están dentro de un dominio no os aparecerá la casilla para marcar a un usuario como menor. Esto también nos ocurrirá si somos un poco geeks y tenemos nuestro propio dominio Home Server en casa.

Cuando un equipo está dentro de un dominio de Windows existe una política que deshabilita por defecto el filtro familiar. Tiene cierto sentido, pues no suele ser necesario tener filtros para niños en una empresa y evita problemas innecesarios a los administradores de sistemas.
Si queremos añadir usuarios infantiles en un equipo de un dominio de Windows tendremos que habilitar la política que nos permite añadir los filtros familiares de nuevo, siguiendo estos pasos desde una cuenta del dominio con privilegios de administrador local de la máquina:

  1. Abrimos el editor de directivas de grupo local:

    editar directiva de grupo

  2. Habilitamos la política de Protección Infantil en Dominio. La encontraremos en Configuración del equipo > Plantillas Administrativas > Componentes de Windows > Protección Infantil:

    ProteccionInfantilDominio

  3. Forzamos el refresco de las políticas de grupo desde la línea de comando; hace falta que el equipo se comunique con el controlador de dominio para activar la directiva. Abrimos un cmd como administrador y ejecutamos la siguiente línea:
    gpupdate /force
  4. Por último, debemos reiniciar el equipo para que los cambios sean efectivos. Tras el reinicio ya podemos añadir un usuario y marcar la casilla de activación de la protección infantil.

Configurar los permisos de la cuenta infantil

Una vez creada la cuenta infantil, debemos recordar que hay que configurar las restricciones que queremos en la cuenta.

La configuración por defecto viene sin filtros ni límites ni restricciones.

protección infantil usuarios

En la configuración del usuario podremos editar todos los límites y restricciones de todo lo que puede hacer el menor en el equipo. configuración de usuario infantil

Como primer paso os recomiendo restringir los sitios web mediante el filtro automático para menores, más adelante podemos ir añadiendo algunos sitios a la listas blancas y negras para refinar el acceso: restricciones web

Y esto es todo por hoy ;)