Datos locales en aplicaciones para Windows Store: Serialización de objetos

En un artículo anterior comentaba una de las posibilidades de almacenamiento de datos en aplicaciones Metro, los diccionarios clave-valor. Esto permitía, recordemos almacenar datos simples de una manera fácil y sencilla, aunque en ocasiones, querremos almacenar datos un poco más complejos. En este artículo se verá cómo almacenar objetos complejos sin tener que recurrir a una base de datos.

Pese a que la API de WinRT incluye todos los componentes necesarios para guardar los datos, hay bibliotecas de clases que nos facilitan mucho la vida. Concretamente esta, Generic Object Storage Helper for WinRT nos permite almacenar objetos y obtenerlos con pocas líneas de código.

Clase a guardar

Para este ejemplo, la clase que almacenaremos será una clase Person. Es requisito que las clases que se almacenen sean serializables, aunque no es necesario que incluyan el atributo. Existen limitaciones, claro, ya que por ejemplo, una ObservableCollection no se puede serializar, una imagen o un objeto de tipo URI tampoco. Además, tiene una ventaja muy interesante, y es que se pueden guardar listas con la misma facilidad, como se verá en el ejemplo.

public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string PictureURL { get; set; }
        public Person Mother { get; set; }
        public Person Father { get; set; }
    }

Una vez obtenido el componente, se puede agregar el componente WinRtUtility.dll a la solución y ya se puede emplear.

Eligiendo el tipo de guardado y almacenando los datos

Podemos guardar los datos en tres espacios diferentes:

  • Local: Los datos se almacenan en el espacio de la aplicación en el dispositivo. Se borran del dispositivo cuando se elimina la aplicación.
  • Roaming: Los datos se almacenan en el espacio del usuario, de esta manera, si se accedes a la app desde otro ordenador donde esté iniciada la sesión con su cuenta Microsoft, se podrán obtener estos mismos datos.
  • Temp: Almacenamiento temporal y volátil, datos que podamos desechar.

Para almacenar los datos, solamente son necesarias las dos líneas siguientes:

var objectStorageHelper = new ObjectStorageHelper<List>(StorageType.Local);
objectStorageHelper.SaveAsync(personObject);

En la primera línea de código se establece el tipo de objeto a guardar así como el espacio en que se hará el guardado. En la segunda es en la que se realiza en guardado del objeto, de manera asíncrona siguiendo con los patrones de WinRT.

Estas líneas se pueden situar en el método OnSuspending, convenientemente situado para que su ejecución sea asíncrona, y situado en el fichero App.xaml.cs:

        private async void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            var objectStorageHelper = new ObjectStorageHelper<List>(StorageType.Local);
            await objectStorageHelper.SaveAsync(Persons);
            deferral.Complete();
        }

Carga de los datos

La carga de los datos es tan sencilla como el guardado, ya que se emplean dos líneas muy similares a las vistas anteriormente:

var objectStorageHelper = new ObjectStorageHelper<List>(StorageType.Local);
Persons = await objectStorageHelper.LoadAsync();

Estas líneas se pueden situar en el método OnLoad, una vez más, modificado para que se ejecute de manera asíncrona:

        protected async override void OnLaunched(LaunchActivatedEventArgs args)
        {
            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                if (args.PreviousExecutionState != ApplicationExecutionState.Suspended)
                {
                    var objectStorageHelper = new ObjectStorageHelper<List>(StorageType.Local);
                    Persons = await objectStorageHelper.LoadAsync();
                }
                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            [...]
            // Ensure the current window is active
            Window.Current.Activate();
        }

Conclusiones

En este artículo se ha visto que existe otra opción para el almacenamiento de estructuras de datos más complejas utilizando poco más que de dos líneas de código situadas en los eventos de carga y guardado de la aplicación.

Más información:

Datos locales en aplicaciones Metro: Diccionarios clave-valor.

Hemos visto en artículos anteriores que es posible acceder a datos de un servicio remoto usando WCF, pero también tendremos casos donde nuestra aplicación necesitará guardar datos de manera local, ya sean opciones de configuración o ficheros locales.

En este artículo veremos cómo guardar datos empleando una de las maneras que nos ofrece WinRT, a través de diccionarios.

Guardando los datos

En primer lugar, agregar la instrucción using Windows.Storage; al fichero, para poder acceder a los detalles de la clase.

Para guardar los datos iremos al método OnSuspending:

void OnSuspending(object sender, SuspendingEventArgs e)
{
    //TODO: Save application state and stop any background activity
}

Dentro de este método accedermos a la variable que contiene la información local de nuestra aplicación:

ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;

En esta variable podremos, entonces, asignar los valores a la misma de esta manera:

localSettings.Values["tipoEntero"] = 10;
localSettings.Values["tipoString"] = "Una cadena de texto";

Con estas líneas, los datos se guardarán cuando la aplicación salga de primer plano. Recordemos que la aplicación no recibirá ningún aviso cuando, por limitaciones de memoria u otras causas la aplicación se cierre.

Cargando los datos

Para acceder a los datos cuando se vuelva a cargar la aplicación, se puede emplear el método OnLaunched, que carga los contenidos que necesita nuestra aplicación, y posteriormente lanza la pantalla inicial:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    ...
}

La manera de acceder a los datos no podía ser más simple, una vez obtenida la variable localSettings como se ha mencionado anteriormente, se pueden acceder a los datos de la siguiente manera:

object valor = localSettings.Values[«valor»];

//Importante comprobar si tenemos el valor, ya que si se intenta hacer un casting antes se provocará una excepción.
if(valor != null)
{
    //Operar con valor (casting, etc...)
}

Conclusiones

Almacenar claves y valores en Metro es una operación muy sencilla que nos permite almacenar las opciones de configuración de nuestras aplicaciones. No es la única, ya que se pueden almacenar y cargar ficheros de texto, o usar estructuras más complejas, dejando esto último para el próximo artículo.

Como extra interesante se recomienda comprender cómo funciona el ciclo de vida de una aplicación Metro, para lo cual recomiendo este artículo de Josué Yeray, donde explica con detalle los diferentes escenarios que se pueden encontrar.

Referencias

Data binding en Windows Forms

Las interfaces de usuario basadas en formularios han permitido pasar de aplicaciones en modo consola a sistemas más interactivos. Esto representa un gran avance de cara a los usuarios, pero desde el punto de vista del desarrollo se puede complicar un poco el código.

En este artículo se verá cómo el acceso a datos o DataBinding proporciona varias ventajas en el desarrollo de las aplicaciones y aumenta la calidad del código. Para el código se usará C# y como herramienta de desarrollo, la versión profesional de Visual Studio 2010, aunque se puede emplear la edición express, completamente gratuita, sin ningún tipo de problemas.

Configuración inicial

Para este ejemplo se creará un nuevo proyecto C#, y se agregarán un par de controles al formulario por defecto, un par de campos de texto TextBox y un botón Button para ejecutar las acciones. El aspecto final del formulario tendrá este aspecto:

Además del formulario, se creará una clase Persona que contendrá dos campos, Nombre y Apellidos, así como un método denominado Guardar. El código es el siguiente:

    public class Persona
    {
        public string Nombre { get; set; }
        public string Apellidos { get; set; }

        public void Guardar() { }
    }

Sin DataBinding

El siguiente paso es mostrar datos en el formulario, para ello se edita el código del constructor para que muestre algunos datos, y luego se edita la acción del botón para que, a partir de los datos del formulario genere una nueva persona y lo guarde:

        public Form1()
        {
            InitializeComponent();
            Persona p1 = new Persona() { Nombre = "Roberto", Apellidos = "Luis" };
            textBox1.Text = p1.Nombre;
            textBox2.Text = p1.Apellidos;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Persona p2 = new Persona();
            p2.Nombre = textBox1.Text;
            p2.Apellidos = textBox2.Text;
            p2.Guardar();
        }

Este código está claro, el problema es que se han perdido 4 líneas en asignar valores desde y hasta dos campos de texto, en un formulario con 10 campos la cosa se puede complicar, y no es por el número de líneas de código, sino porque esta duplicidad puede generar errores al modificar el mismo.

Con DataBinding

La diferencia fundamental del enlace a datos es que se pueden enlazar los campos del formulario de manera directa a las propiedades de una clase, de tal manera que las asignaciones se siguen realizando, pero dentro del código generado por Visual Studio y son bidireccionales, es decir, que cualquier cambio en el formulario afecta directamente a las propiedades de la clase.

Para enlazar el formulario a la clase es necesario, con el formulario en modo diseño, seleccionar el primer campo a enlazar (en nuestro caso textBox1), desplazarse a la ventana propiedades, situada en la esquina inferior derecha, o con la secuencia Alt + Enter.

En esta ventana, se selecciona la opción DataBindings, y dentro de esa opción, el campo Text, no se sorprenda al no encontrar nada, ya que deberemos agregar el origen de datos al proyecto, pulsando en el botón Add Project Data Source…, el cual mostrará el asistente para la selección del origen de datos.

En este asistente se selecciona Object como el origen de datos del control:

Dentro de este origen se selecciona la clase Persona (Nota, si no aparece la clase es que no ha recompilado el proyecto, tras compilarlo y volver a ejecutar el asistente se mostrará la clase).

Al finalizar el asistente, al volver a ir al menú, el contenido no estará vacío, sino que se podrá acceder a los campos de la clase sin problemas, y asignarlos al valor Text del control. Al asginarlo por primera vez se mostrará un nuevo control en el diseñador, llamado personaBindingSource, que es el objeto que realiza la magia del enlace de datos. De esta manera se puede asignar los valores a los 2 campos de texto sin problemas.

Finalmente queda editar el código para sustituir las asignaciones por una única asignación al objeto personaBindingSource, y obtener la información del mismo:

        public Form1()
        {
            InitializeComponent();
            personaBindingSource.DataSource = new Persona() { Nombre = "Roberto", Apellidos = "Luis" };
        }

        private void button1_Click(object sender, EventArgs e)
        {
           (personaBindingSource.DataSource as Persona).Guardar();
        }

Hemos eliminado 4 líneas del código, y la validación se efectúa directamente en la clase. Esto permite que se puedan usar patrones como MVC en aplicaciones Windows Forms.

Conclusiones:

Windows Forms, aunque ha pasado a segundo plano con el auge de WPF, silverlight y las interfaces metro, se sigue empleando para soporte de aplicaciones existentes y como manera de creación de nuevas soluciones. Este método permite tener una codificación más limpia, liberar el código del formulario, y que la clase tenga la responsabilidad de validar datos y de mantener su integridad. Es verdad que esto implica cierto acoplamiento, pero la ventaja es que cualquier clase que herede de Persona podrá ser enlazada sin más que cambiar dos líneas de código.

Más información:

  • Descarga el ejemplo terminado aquí