Programación Al Limite Headline Animator

viernes, 30 de diciembre de 2011

[Serialización Binaria] Guardar Estado en la aplicación.



Usando la serialización para guardar el estado de una aplicación.



En la siguiente practica se utilizan los siguientes espacio de nombres:


using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;


Cuando trabajamos en una aplicación en ciertas ocasiones deseamos guardar el estado de una aplicación, en C# y en la tecnología .NET podemos hacerlo de diferentes formas, una de ellas es usando la llamada Configuración de la aplicación(Settings), entre otras. Esta vez utilizaremos la llamada técnica Serialización, la cual es una técnica que nos permite guardar el estado de objetos, clases, etc. En un formato binario u otro formato como XML y luego recuperarlo para un posterior uso.
Según Wikipedia la serialización es:

La serialización (o marshalling en inglés) consiste en un proceso de codificación de un Objeto (programación orientada a objetos) en un medio de almacenamiento (como puede ser un archivo, o un buffer de memoria) con el fin de transmitirlo a través de una conexión en red como una serie de bytes o en un formato humanamente más legible como XML o JSON, entre otros.

En esta ocasión utilizaremos esta técnica de serialización binaria para poder guardar el estado en una aplicación en un archivo binario (.BIN) y posteriormente recuperarlo para mostrar el estado guardado de una aplicación. Vamos a comenzar a desarrollar la aplicación:

Primer Paso - Creación Formulario


Vamos a crear un formulario parecido al siguiente mostrado en la ilustración:

Como podemos notar, el formulario cuenta con tres componentes, los cuales son: Un Label y dos Buttons. Lo que pretendemos hacer con la siguiente aplicación es cambiar el tamaño y fuente del texto escrito en el Label, y que al cerrar la aplicación, el estado actual de los componentes se mantengan intactos.

Segundo Paso - Crear clase Serializable

En el segundo paso crearemos la clase, la cual estará conformada por el Atributo de metadatos llamado [Serializable], el cual le permite a una determinada clase ser serializada, sin este atributo sería imposible que una determinada clase pueda ser serializada. Veamos la ilustración de la clase:

[Serializable]
public class Personalizacion
{
private Color color;
private Font fuente;


public Color Color
{
get
{
return this.color;
}

set
{
this.color = value;
}
}
public Font Fuente
{
get
{
return this.fuente;
}

set
{
this.fuente = value;
}
}

Como vemos, hemos creado una clase con los atributos Color y Fuente, y lo hemos declarado de manera privada (No es obligatorio) y hemos utilizado las propiedades para poder obtener y establecer valores sobre estos atributos. Esta clase es aquella que se va a serializar, esto implica que su "estado" será guardado en un archivo binario, para posteriormente ser cargado a la aplicación.

Tercer Paso - Crear los atributos de la clase principal y crear los métodos Serializar y Deserializar.


En este paso nos dirigimos a la clase principal y creamos los siguientes atributos.


public class Form1: Form
{
private BinaryFormatter FormatoBinario;
private FileStream FlujoArchivo;
private ColorDialog SelectorColor;
private FontDialog SelectorFuente;
}

Creamos el atributo BinaryFormatter, que es aquella clase que nos permitirá serializar la clase Personalizar. El siguiente atributo es la clase FileStream, es aquella clase que nos permitirá crear el archivo en donde serializaremos la clase. Los dos ultimos atributos, son las clases ColorDialog y FontDialog, que son aquellas que nos permitirán seleccionar el color y la fuente para nuestro Label.

Lo siguiente será crear los métodos serializar y deserializar en nuestra clase principal. Primero comenzaremos con el método serializar.


public void Serializar(FontDialog SelFuente, ColorDialog SelColor)
{
Personalizacion PersonalizarClass = new Personalizacion();
FormatoBinario = new BinaryFormatter();
try
{
FlujoArchivo = File.Create("Personalizar.BIN");
PersonalizarClass.Color = SelColor.Color;
PersonalizarClass.Fuente = SelFuente.Font;
FormatoBinario.Serialize(FlujoArchivo, PersonalizarClass);
FlujoArchivo.Close();
}
catch (SerializationException)
{
MessageBox.Show("Error de serialización");
}
}

En este método creamos una instancia de la clase Personalizar y al mismo tiempo instanciamos la clase BinaryFormatter. Lo siguiente será crear un bloque Try/Catch para poder capturar las excepciones que puedan ocurrir durante el proceso de serialización. En el bloque Try lo primero que haremos será darle el valor a nuestro atributo o variable FlujoArchivo, el Path o ruta en donde queremos crearemos nuestro archivo de serialización, utilizando la clase estática File y su método estático Create, que permite crear un archivo en una determinada ruta, en este caso se creara en la carpeta Debug/Depurar de nuestro proyecto. Como pudimos notar al crear este método, a este le hemos creado dos parámetros de tipo FontDialog y ColorDialog, estos representaran los valores para los atributos Color y Fuente de la clase Personalizar, logrando así, que el valor que seleccione el usuario en los distintos selectores, sea el valor de esas dos variables o atributos de esa determinada clase. Por ultimo, utilizamos el método Serialize para serializar nuestra clase. Este métodos nos pide dos parámetros, el primero es el Flujo de archivo o stream en donde se encuentra nuestra ruta de archivo, y el segundo parámetro es el objeto a serializar, en este caso le pasamos como parámetro la clase personalizar y por ultimo cerramos el archivo mediante el método Close(). En el bloque Catch, capturamos una excepción de tipo SerializationException. En conclusión a esto, lo que hacemos es, por ejemplo, que si seleccionamos un tipo de fuente, ejemplo "Calibri" y un color, ejemplo, "Azul", estos dos valores se guardaran en el archivo de serialización en un formato binario, para así mantener el estado de la clase y por ende la aplicación.

Y ahora creamos el método deserializar.


public void Deserializar()
{
Personalizacion PersonalizarClase = new Personalizacion();
FormatoBinario = new BinaryFormatter();
try
{
if (File.Exists("Personalizar.BIN"))
{
FlujoArchivo = File.OpenRead("Personalizar.BIN");
PersonalizarClase = (Personalizacion)FormatoBinario.Deserialize(FlujoArchivo);
lblmensaje.ForeColor = PersonalizarClase.Color;
lblmensaje.Font = PersonalizarClase.Fuente;
FlujoArchivo.Close();

}
}

catch (SerializationException)
{
return;
}

}

El método deserializar es aquel que nos permitirá es cargar el estado anterior del objeto serializado, en nuestro ejemplo, la clase Personalizar. Es parecido al método serializar, creamos una instancia de la clase Personalizar y de la clase BinaryFormatter. Luego un bloque Try/Catch para capturar las excepciones de tipo SerializationException. Dentro del bloque Try, creamos una condición indicando que si el archivo "Personalizar.BIN" existe realice la operación de Deserialización. En este caso al flujo de archivo le indicamos que lea la información del archivo mediante el método OpenRead de la clase File. Luego a la clase le asignamos el valor que retorna la función Deserialize de la clase BinaryFormatter, no sin antes hacer un CAST, convirtiendo el valor de retorno en un valor tipo de la clase Personalizar. Hacemos lo anterior debido a que el valor de retorno, nos retornara los valores anteriores establecidos por el usuario, a las variables o atributos, Color y Fuente de la clase personalizar, llevándolos a un estado anterior.

Ya tenemos los dos métodos mas importantes para lograr el objetivo de la aplicación, ahora procederemos a colocar los métodos dentro de los eventos Click de los Buttons de la aplicación.


Cuarto Paso - Utilizando los métodos creados.



private void btnCambiarFuentes_Click(object sender, EventArgs e)
{
DialogResult Resultado = SelectorFuente.ShowDialog();
if (Resultado == DialogResult.Cancel)
{
return;
}
else
{
lblmensaje.Font = SelectorFuente.Font;
}
}

private void btnCambiarColor_Click(object sender, EventArgs e)
{

DialogResult Resultado = SelectorColor.ShowDialog();
if (Resultado == DialogResult.Cancel)
{
return;
}
else
{
lblmensaje.ForeColor = SelectorColor.Color;
}

}

Este paso aquí no necesita mucha explicación, simplemente usamos las variables o atributos de la clase creadas, estas son el selector de colores y el selector de fuente, se las asignamos como valor a una propiedad de tipo DialogResult
y luego hacemos condiciones estableciendo que si el valor de retorno de los selectores es Cancelado, entonces no retorne nada y así se mantenga el estado actual de los componentes, y de lo contrario a esto que se le aplique la fuente y el color al componente Label.


Ultimo Paso - Instanciando en el constructor de la clase y confirmando la serialización



public Form1()
{
InitializeComponent();
SelectorColor = new ColorDialog();
SelectorFuente = new FontDialog();
}

En este paso lo que hacemos es instanciar las clases de los selectores de colores y fuente en el constructor de la clase principal. Hacemos esto para que la aplicación no nos de una Excepción de Tipo "Referencia no establecida como instancia de un objeto", ya que en el método serializar no hemos instanciado los parámetros de selección de colores, ya que en un método posterior utilizaremos dicho método para serializar el estado actual.

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult Resultado = MessageBox.Show("¿Deseas guardar los cambios realizados en el Label?",
"Guardar Cambios", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (Resultado == DialogResult.Yes)
{
Serializar(SelectorFuente, SelectorColor);

}
}

Por ultimo, usamos el evento FormClosing del formulario, que es aquel que se produce al cerrar el formulario de la aplicación. Lo que hacemos aquí es preguntarle al usuario que si desea cambiar los cambios realizados en el Label, si el usuario elige la opción "SI", entonces se usa el método serializar para serializar los valores de los dos selectores de dialogo. Por esta razón se instanciaron las dos clases en el constructor, así evitamos una excepción.

Lo siguiente es agregar el método deserializar en el evento Load del formulario, así logramos que al iniciar la aplicación se inicialize o se cargue el estado anterior del Label.

private void Form1_Load(object sender, EventArgs e)
{
Deserializar();
}


Ahora, solo les queda ejecutar la aplicación y ver los resultados.Trate de cambiar la fuente y el color del Label e intente cerrar y abrir nuevamente la aplicación para ver los resultados.

Así debería quedar la clase principal:


public partial class Form1 : Form
{
private BinaryFormatter FormatoBinario;
private FileStream FlujoArchivo;
private ColorDialog SelectorColor;
private FontDialog SelectorFuente;
public Form1()
{
InitializeComponent();
SelectorColor = new ColorDialog();
SelectorFuente = new FontDialog();
}

private void Form1_Load(object sender, EventArgs e)
{
Deserializar();
}

private void button1_Click(object sender, EventArgs e)
{
DialogResult Resultado = SelectorFuente.ShowDialog();
if (Resultado == DialogResult.Cancel)
{
return;
}
else
{
lblmensaje.Font = SelectorFuente.Font;
}
}

private void button2_Click(object sender, EventArgs e)
{

DialogResult Resultado = SelectorColor.ShowDialog();
if (Resultado == DialogResult.Cancel)
{
return;
}
else
{
lblmensaje.ForeColor = SelectorColor.Color;
}

}

public void Serializar(FontDialog SelFuente, ColorDialog SelColor)
{
Personalizacion PersonalizarClass = new Personalizacion();
FormatoBinario = new BinaryFormatter();
try
{
FlujoArchivo = File.Create("Personalizar.BIN");
PersonalizarClass.Color = SelColor.Color;
PersonalizarClass.Fuente = SelFuente.Font;
FormatoBinario.Serialize(FlujoArchivo, PersonalizarClass);
FlujoArchivo.Close();
}
catch (SerializationException)
{
MessageBox.Show("Error de serialización");
}

}

public void Deserializar()
{
Personalizacion PersonalizarClase = new Personalizacion();
FormatoBinario = new BinaryFormatter();
try
{
if (File.Exists("Personalizar.BIN"))
{
FlujoArchivo = File.OpenRead("Personalizar.BIN");
PersonalizarClase = (Personalizacion)FormatoBinario.Deserialize(FlujoArchivo);
lblmensaje.ForeColor = PersonalizarClase.Color;
lblmensaje.Font = PersonalizarClase.Fuente;
FlujoArchivo.Close();

}
}

catch (SerializationException)
{
return;
}

}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult Resultado = MessageBox.Show("¿Deseas guardar los cambios realizados en el formulario?",
"Guardar Cambios", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (Resultado == DialogResult.Yes)
{
Serializar(SelectorFuente, SelectorColor);

}
}

}
}

Aquí terminamos con la practica de "Serialización Binaria", espero que le sea de ayuda en futuros proyectos y puedan sacarle el maximo a esta técnica.




1 comentario:

  1. este es el error que e lanza al ejecutarlo Excepción no controlada del tipo 'System.IO.IOException' en mscorlib.dll

    Información adicional: El proceso no puede obtener acceso al archivo 'C:\Users\yorbin\Documents\Visual Studio 2013\system hostpital\system hostpital\bin\Debug\Serializable.BIN' porque está siendo utilizado en otro proceso.

    ResponderEliminar