Blog alskare

.Net y lo que surja

Archive for 15 de septiembre de 2009

C# Mantener la configuración de tamaño y posición de los formularios

Posted by alskare en 15/09/2009

No sería la primera vez que los clientes piden que, en los formularios WinForms, se guarde tanto la posición como el tamaño del formulario, incluso después de cerrar la aplicación. Bajo esta premisa, me inicio en un nuevo post que tiene como finalidad, precisamente esto, almacenar la información de tamaño del formulario y recuperarla cada vez que se abra de nuevo el mismo formulario.

Framework.Net nos ofrece una serie de posibilidades amplias para poder guardar información en el área de trabajo del usuario. Una de estas opciones es el namespace System.XML, el cual nos permite poder trabajar con ficheros XML, tanto a nivel de lectura como de escritura. Aprovechando estas características, se me ocurre la creación de un fichero en el que poder almacenar los datos de tamaño y posición del formulario. Ahora bien, el problema de una aplicación es la cantidad de formularios existentes, así que empiezo a pensar en un fichero con una estructura similar a la siguiente:

<?xml version="1.0" encoding="utf-8"?>
<Configuracion>
    <NombreForm1>
        <WindowState>ValorWindowStateForm1</WindowState>
        <Top>ValorTopForm1</Top>
        <Left>ValorTopForm1</Left>
        <Width>ValorTopForm1</Width>
        <Height>ValorTopForm1</Height>
    </NombreForm1>
    <NombreForm2>
        <WindowState>ValorWindowStateForm2</WindowState>
        <Top>ValorTopForm2</Top>
        <Left>ValorTopForm2</Left>
        <Width>ValorTopForm2</Width>
        <Height>ValorTopForm2</Height>
    </NombreForm2>
</Configuracion>

De esta manera, si los datos de cada formulario representa un nodo dentro del archivo XML, puede resultar más o menos sencilla la adaptación de la librería a cualquier otro valor.

Una vez está decidido cuál será el medio de almacenamiento, cabe comenzar a pensar en cómo se realizarán las operaciones de lectura y escritura sobre el fichero XML, así que, recordando las clases de Programación Orientada a Objeto, se me ocurre la idea de crear un formulario base del que heredarán todos aquellos formularios que necesiten almacenar dicha información.

Manos a la obra. Comenzamos creando un nuevo proyecto que será una Biblioteca de controles de WindowsForms, que contendrá un formulario denominado FormularioBase. Éste será un formulario estándar en el que primarán dos cosas:

  1. En el evento FormClosing se almacenarán los datos que interesen en el fichero de configuración
  2. En el evento Load se recuperarán, en caso de existir previamente, los datos de configuración y se aplicarán al formulario

Así, una vez creado el campo que contendrá el fichero de configuración,

private string ficheroXML = Path.Combine(Application.StartupPath, "ConfForm.xml");

nos ponemos a implementar el proceso de almacenamiento de los datos:

/// <summary>
 /// Almacena la información del formulario actual en fichero XML
 /// </summary>
 private void GuardaConfiguracionForm()
 {
     string nombreForm = this.Name;
     XmlNode nodoPrincipal;

     try
     {
         // Intancia del documento XML que permitirá guardar la configuración
         XmlDocument doc = new XmlDocument();

         // Se comprueba que exista el fichero
         if (!File.Exists(ficheroXML))
         {
             // No existe el fichero. Se añaden los nodos iniciales
             doc.AppendChild(doc.CreateComment("Configuración de formularios - älskare, Sep/09"));

             // Creación del nodo principal
             nodoPrincipal = doc.CreateNode(XmlNodeType.Element, "Configuracion", null);

             // Se añade el nodo principal al documento
             doc.AppendChild(nodoPrincipal);
         }
         else
         {
             // El fichero existe previamente. Se recupera la información
             doc.Load(ficheroXML);

             // Lectura del nodo principal
             nodoPrincipal = doc.SelectSingleNode("Configuracion");
         }

         // Se comprueba si existe un nodo con el nombre del form actual
         XmlNode nodoFormActual = nodoPrincipal.SelectSingleNode(nombreForm);
         if (nodoFormActual == null)
             nodoFormActual =
                   nodoPrincipal.AppendChild(
                     doc.CreateNode(XmlNodeType.Element, nombreForm, null));

         // Almacenamiento de los valores
         NodoValor(doc, nodoFormActual, "WindowState", this.WindowState);
         NodoValor(doc, nodoFormActual, "Top", this.Top);
         NodoValor(doc, nodoFormActual, "Left", this.Left);
         NodoValor(doc, nodoFormActual, "Width", this.Width);
         NodoValor(doc, nodoFormActual, "Height", this.Height);


         // Se guarda el fichero de configuración en disco
         XmlTextWriter tw = new XmlTextWriter(ficheroXML, Encoding.UTF8);
         tw.Indentation = 4;
         tw.IndentChar = " "[0];
         tw.Formatting = Formatting.Indented;

         doc.Save(tw);
         doc = null;

         tw.Flush();
         tw.Close();
     }
     catch { }
 }

 /// <summary>
 /// Crea un nuevo par clave-valor en el nodo actual en caso de que no exista
 /// <returns></returns>
 XmlNode NodoValor(XmlDocument doc, XmlNode nodoPadre, string Clave, Object Valor)
 {
     XmlNode nodoActual = nodoPadre.SelectSingleNode(Clave);
     if (nodoActual == null)
         nodoActual = nodoPadre.AppendChild(doc.CreateNode(XmlNodeType.Element, Clave, null));
     nodoActual.InnerText = Valor.ToString();
     return nodoActual;
 }

 

Una vez realizado el proceso de escritura del fichero, falta poder recuperarlo para aplicar los cambios en el formulario cuando se abra.

/// <summary>
/// Realiza una lectura del fichero de configuración y aplica los cambios al form
/// </summary>
private void LecturaConfiguracionForm()
{
    // No se controlan errores. En caso de existir cualquier error
    // no se modifica nada en el formulario actual
    try
    {
        string nombreForm = this.Name;
        XmlDocument doc = new XmlDocument();
        doc.Load(ficheroXML);

        // Lectura del nodo principal
        XmlNode nodoPrincipal = doc.SelectSingleNode("Configuracion");

        // Lectura del nodo del formulario actual
        XmlNode nodoActual = nodoPrincipal.SelectSingleNode(nombreForm);

        // Lectura del modo de presentación de la ventana
        this.WindowState = (FormWindowState)Enum.Parse(typeof(FormWindowState), GetValor(nodoActual, "WindowState").ToString(), false);

        if (this.WindowState == FormWindowState.Normal)
        {
            this.Top = Convert.ToInt32(GetValor(nodoActual, "Top"));
            this.Left = Convert.ToInt32(GetValor(nodoActual, "Left"));
            this.Width = Convert.ToInt32(GetValor(nodoActual, "Width"));
            this.Height = Convert.ToInt32(GetValor(nodoActual, "Height"));
        }
    }
    catch { }
}

/// <summary>
/// Obtiene el valor de la clave
/// </summary>
private object GetValor(XmlNode nodoPadre, string clave)
{
    XmlNode valor = nodoPadre.SelectSingleNode(clave);
    return valor.LastChild.Value;
}

Implementadas tanto la escritura como la lectura de los valores, quedará aplicar dichos cambios al formulario actual. En esta parte, debo reconocer que no he conseguido eliminar un efecto un tanto raro que se da al abrir el formulario, sobre todo, si el formulario a mostrar está contenido en otro en modo MDI. Seguiremos trabajando sobre el tema para ver si consigo eliminar dicho efecto antiestético.

public FormularioBase()
{
    this.SuspendLayout();
    InitializeComponent();
    this.ResumeLayout();

    this.Load += new EventHandler(FormularioBase_Load);
    this.FormClosing += new FormClosingEventHandler(FormularioBase_FormClosing);
}

void FormularioBase_Load(object sender, EventArgs e)
{
    LecturaConfiguracionForm();
}
void FormularioBase_FormClosing(object sender, FormClosingEventArgs e)
{
    GuardaConfiguracionForm();
}

Bueno, pues ya está. A partir de ahora, cada vez que cree un nuevo WinForms, tan sólo añadiré una referencia a la librería en la que tengo este formulario base y, por supuesto, haré que cada formulario herede de FormularioBase.

public partial class Form2 : jnSoftware.Utiles.FormularioBase 
    {
        public Form2()
        {
            InitializeComponent();
        }
    }

 

Si le interesa a alguien, dejo enlace en el que se puede descargar un ejemplo de uso: descargar.

Posted in C#, Controles, WinForms | 2 Comments »