Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

Manejar los valores almacenados en AppSettings

Primera parte:
Cómo recuperar los valores en tiempo de ejecución, incluso valores personalizados, y cómo guardarlos al cerrar el formulario.

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)

 

Publicado el 21/Feb/2005
Actualizado el 21/Feb/2006
Autor: Guillermo 'guille' Som


En este artículo te explicaré cómo indicarle al diseñador de formularios que cree un fichero de configuración para nuestra aplicación, el cual podrás usar en tiempo de ejecución para recuperar ciertos valores, los cuales puedes modificar una vez que el proyecto esté compilado.

También veremos cómo guardar los nuevos valores que tengan esas propiedades, para que de esta forma, el usuario siempre tenga los valores que usó por última vez.
Además vamos a añadir valores "personalizados" (los que el diseñador no contempla), para que nuestra aplicación sea más "dinámica".

 

Nota del 21/Feb/2006:
Aquí tienes una clase (versión mejorada de este código) para usar con VB2005

 


Introducción

Si has trasteado con las opciones de la ventana de propiedades de los formularios Windows.Forms, seguramente te habrás encontrado con una propiedad llamada "DynamicProperties", en la cual podemos indicar que propiedades de cada uno de los controles del formulario queremos "hacer persistentes", es decir, que se "lean" de un fichero, en lugar de usar los valores asignados en tiempo de diseño, cada vez que el formulario se carga.

Seguramente conocerás la existencia de esa propiedad y, puede que hasta lo hayas usado, pero casi seguro que te habrá defraudado un poco al no poder guardar los cambios o, al menos, no poder indicar otros valores salvo los indicados por el diseñador de formularios Windows.Forms.

Antes de pasar a detalles mayores, veamos cómo puedes guardar esos valores y recuperarlos al iniciarse la aplicación. Aunque como verás, esto último es totalmente automático.

Crear el fichero de configuración

Lo primero que debemos hacer es indicarle al diseñador de formularios que queremos mantener ciertos valores en un fichero de configuración.

Para lograr esto, debemos seguir ciertos pasos.
Para seguir estos pasos, he creado un proyecto muy simple con unos cuantos controles, tal como te muestro en la figura 1:
 

Figura 1. El formulario en tiempo de diseño

Figura 1. El formulario en tiempo de diseño


Los nombres usados para los controles y el formulario son los mostrados en la figura, que serían los que el propio diseñador les daría al crearlos... esto no es recomendable, ya que cada control debería tener el nombre para lo que está pensado, pero, mi excusa es que lo he hecho así para mantener las cosas simples y sencillas...

Supongamos que nos interesa que el título de la aplicación (el formulario realmente), lo podamos modificar para que muestre lo que hayamos indicado en el fichero de configuración.
Lo primero que debemos hacer es asegurarnos de que el formulario esté seleccionado y mostrar la ventana de propiedades (si no la ves, pulsa F4), en esa ventana, seleccionaremos la propiedad DynamicProperties y pulsaremos en el + para que nos muestre la opción "(Avanzado)", que tendrá un botón con tres puntos (suspensivos), tal como podemos ver en la figura 2:
 

Figura 2. La ventana de propiedades

Figura 2. La ventana de propiedades


Al pulsar en dicho botón, el diseñador nos mostrará un cuadro de diálogos con las propiedades del control seleccionado, (en este caso del formulario), que podemos elegir para que se almacenen en el fichero de configuración.
En nuestro caso, le indicaremos que queremos "persistir" el valor de la propiedad Text, por tanto seleccionamos esa propiedad de la lista de la izquierda y en el combo de la derecha tendremos el nombre que el diseñador usará para almacenar en el fichero dicho valor, por defecto te mostrá el nombre usando el formato "nombre del objeto" punto "nombre de la propiedad", pero puedes escribir el que más te guste, aunque en este ejemplo, usaré los valores predeterminados, en el caso de la propiedad Text del formulario, el nombre es: Form1.Text, tal como podemos ver en la figura 3:
 

Figura 3. El cuadro de propiedades dinámicas

Figura 3. El cuadro de propiedades dinámicas


Una vez que hemos pulsado en aceptar, ocurrirán varias cosas, la primera que notaremos es que ahora en la ventana de propiedades, justo debajo de (Avanzado) aparecerá la propiedad que queremos usar y el nombre que tendrá en el fichero de configuración.
Otra de las cosas que notaremos enseguida es que en el explorador de soluciones, (tal como podemos comprobar en la figura 4), aparece un nuevo fichero: app.config, en este fichero estarán las propiedades que se asignarán en tiempo de ejecución.
 

Figura 4. El explorador de soluciones

Figura 4. El explorador de soluciones


El contenido del fichero app.config, después de haber indicado otras propiedades del resto de los controles, tendrá el siguiente aspecto:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration>
   <appSettings>
	<!-- La configuración de la aplicación de usuario y de la propiedad configurada van aquí.-->
	<!-- Ejemplo: <add key="settingName" value="settingValue"/> -->
	<add key="Form1.Text" value="Form1" />
	<add key="CheckBox1.Checked" value="False" />
	<add key="RadioButton1.Checked" value="True" />
	<add key="RadioButton2.Checked" value="False" />
	<add key="TextBox1.Text" value="TextBox1" />
   </appSettings>
</configuration>

Cada vez que añadimos nuevas propiedades, se actualiza este fichero, y los valores que en él haya serán los que se usarán cuando la aplicación se inicie, aunque en ese caso, no se utiliza este fichero, sino otro creado en el mismo directorio de la aplicación, el cual tendrá el siguiente nombre:
<nombre del ejecutable>.exe.config, de la creación de ese fichero no debes preocuparte, ya que el propio Visual Studio se encarga de hacerlo, de lo que si debes preocuparte es de distribuirlo con el ejecutable, ya que si no lo haces, se producirá una excepción al ejecutar el programa, ya que no se ha encontrado dicho fichero y el código extra que el diseñador añade automáticamente, espera encontrarse con él.

 

Nota:
Si te fijas, en la ventana de propiedades aparecerá una marca junto a la propiedad que hemos asignado para guardar en el fichero de configuración.

 

¿Qué código extra añade el diseñador de formularios?

Pues este que te muestro a continuación, (tanto para VB como para C#), el cual estará en la parte "oculta" del código generado por el diseñador y, como norma, suele estar junto al resto de asignaciones de propiedades de cada control, aquí te muestro solamente el del Form1:
 

' En VB:
Me.Text = CType(configurationAppSettings.GetValue("Form1.Text", GetType(System.String)), String)


// En C#:
this.Text = ((string)(configurationAppSettings.GetValue("Form1.Text", typeof(string))));

 

La variable "configurationAppSettings" es del tipo System.Configuration.AppSettingsReader que el diseñador crea en el método InitializeComponent.

 

¿Cómo usamos los valores del fichero de configuración en tiempo de ejecución?

Para usar esos valores no tienes que hacer nada... ya que, como acabo de mostrar anteriormente, es el propio diseñador de Windows.Forms el que se encarga de leer y usar esos valores.

Lo que si debes tener cuidado es cuando modifiques el contenido del fichero de configuración, al menos si vas a usar acentos (tildes), eñes o cualquier otro carácter "raro", ya que el formato del fichero es UTF-8 y así debe ser guardado para usar otros caracteres. Creo que el Bloc de notas de Windows respeta ese formato, pero puede que no todos los editores de textos.

 

Guardar los nuevos valores para "reutilizarlos" en tiempo de ejecución

Aquí empezaremos a complicarnos un poco la vida, ya que lo visto hasta ahora es algo automático y, realmente no tenemos que hacer nada de nada... bueno, sí, asignar los valores de las propiedades que queremos guardar en el fichero de configuración.

Pero si esos valores no "persisten" mientras estemos en ejecución... pues la verdad es que en cierto modo es una característica poco útil, o al menos no todo lo útil que debería ser, ya que lo interesante es que si el usuario de nuestra aplicación cambia algunos de esos valores, la próxima vez que la utilice, se muestren los cambios que él realizó.

Para poder hacer esto, tendremos que echar mano de las clases incluidas en el espacio de nombres System.Xml, en particular de las clases XmlDocument y XmlNode.

Antes de mostrarte el código, te explico lo que, (según yo), tenemos que hacer:

  1. Crear un objeto del tipo XmlDocument y cargar el fichero de configuración.
  2. Crear un métodos para guardar los valores.
  3. Cuando se cierre el formulario (método Closing) asignar los valores y guardar el fichero.

El primer punto, realmente consta de varios pasos, ya que también tenemos que indicar qué fichero es el que se usará para almacenar la información de la configuración. Pero creo que si vemos el código será más fácil de comprender:
(aquí te muestro el de Visual Basic, un poquito más abajo tienes el código de C#)

 

Código para Visual Basic:

Private configXml As New XmlDocument
Private ficConfig As String
'
Public Sub New()
    MyBase.New()

    'El Diseñador de Windows Forms requiere esta llamada.
    InitializeComponent()

    'Agregar cualquier inicialización después de la llamada a InitializeComponent()
    '
    ficConfig = Application.ExecutablePath & ".config"
    configXml.Load(ficConfig)
    '
End Sub


' guardar los nuevos valores en la configuración
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
    cfgSetValue("configuration/appSettings", "CheckBox1.Checked", Me.CheckBox1.Checked.ToString)
    cfgSetValue("configuration/appSettings", "RadioButton1.Checked", Me.RadioButton1.Checked.ToString)
    cfgSetValue("configuration/appSettings", "RadioButton2.Checked", Me.RadioButton2.Checked.ToString)
    cfgSetValue("configuration/appSettings", "TextBox1.Text", Me.TextBox1.Text)
    cfgSetValue("configuration/appSettings", "Form1.Text", Me.Text)
    '
    configXml.Save(ficConfig)
End Sub


' El método para guardar los valores
Private Sub cfgSetValue(ByVal seccion As String, _
                        ByVal clave As String, _
                        ByVal valor As String)
    '
    Dim n As XmlNode
    n = configXml.SelectSingleNode(seccion & "/add[@key=""" & clave & """]")
    If Not n Is Nothing Then
        n.Attributes("value").InnerText = valor
    End If
End Sub

 

 

Código para C#:

//
private XmlDocument configXml = new XmlDocument();
private string ficConfig;
//
public Form1()
{
    //
    // Necesario para admitir el Diseñador de Windows Forms
    //
    InitializeComponent();

    //
    // TODO: agregar código de constructor después de llamar a InitializeComponent
    //
    ficConfig = Application.ExecutablePath + ".config";
    configXml.Load(ficConfig);
    //
}


//
// guardar los nuevos valores en la configuración
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
{
    cfgSetValue("configuration/appSettings", "CheckBox1.Checked", this.CheckBox1.Checked.ToString());
    cfgSetValue("configuration/appSettings", "RadioButton1.Checked", this.RadioButton1.Checked.ToString());
    cfgSetValue("configuration/appSettings", "RadioButton2.Checked", this.RadioButton2.Checked.ToString());
    cfgSetValue("configuration/appSettings", "TextBox1.Text", this.TextBox1.Text);
    cfgSetValue("configuration/appSettings", "Form1.Text", this.Text);
    //
    configXml.Save(ficConfig);
}  


//
//  El método para guardar los valores
private void cfgSetValue(string seccion, string clave, string valor) 
{
    //
    XmlNode n;
    n = configXml.SelectSingleNode(seccion + "/add[@key=\"" + clave + "\"]");
    if( n != null )    {
        n.Attributes["value"].InnerText = valor;
    }
}

 

Para saber el nombre del fichero de configuración, lo único que tenemos que hacer es añadirle .config al final del nombre y asunto arreglado:

ficConfig = Application.ExecutablePath & ".config"

Después le indicamos al objeto XmlDocument que lo cargue, esto lo hacemos mediante el método Load:

configXml.Load(ficConfig)

Cuando se cierre el formulario, tenemos que encargarnos de guardar los nuevos valores, para ello llamamos al método cfgSetValue indicando que valores debe guardar.
Los parámetros de este método son: el "nodo" (o sección) en el que debe guardarse, la clave y el valor a asignar.

En el método cfgSetValue seleccionamos el nodo adecuado, es decir, <add y el atributo key y si dicha clave (y atributo) existen, asignamos el valor al atributo value.

Aquí el truco está en la forma de seleccionar el nodo, ya que si no indicamos el atributo key de esa forma... pues no lo encontrará, (no sabes tú la de vueltas que he dado hasta "saber" cómo usar esas dos puñeteras líneas de código, sobre todo la última...), además de usar la propiedad Attributes del nodo hallado para asignar el valor a value.

Una vez que hemos asignado todos los valores, debemos guardar el contenido del objeto configXml para que se mantenga en el fichero de configuración, como habrás adivinado eso lo hacemos mediante el método Save de la clase XmlDocument.

 

Una toma de precaución

Como te comenté antes, si el usuario ha borrado "accidentalmente" el fichero de configuración, se producirá una excepción y el programa se detendrá, así que te recomiendo que compruebes que ese fichero existe, y en caso de que no exista, asignes los valores que creas conveniente, algo así:

Public Sub New()
    MyBase.New()

    'El Diseñador de Windows Forms requiere esta llamada.
    InitializeComponent()

    'Agregar cualquier inicialización después de la llamada a InitializeComponent()
    '
    ficConfig = Application.ExecutablePath & ".config"
    '
    ' comprobar si existe el fichero de configuración
    If System.IO.File.Exists(ficConfig) = False Then
        cfgSetValue("configuration/appSettings", "CheckBox1.Checked", "False")
        cfgSetValue("configuration/appSettings", "RadioButton1.Checked", "True")
        cfgSetValue("configuration/appSettings", "RadioButton2.Checked", "False")
        cfgSetValue("configuration/appSettings", "TextBox1.Text", "Hola, Mundo")
        cfgSetValue("configuration/appSettings", "Form1.Text", "Form1")
        '
        configXml.Save(ficConfig)
    End If
    '
    configXml.Load(ficConfig)
    '
End Sub

Aunque deberías quitar el código que el diseñador de formularios ha creado automáticamente, ya que ese código se ejecuta antes que el nuestro... por tanto también daría error.
Y no vale poner este código antes de InitializeComponents, ya que también daría error al usar el método Save; el porqué, es que ¡no hay código XML que guardar!, ya que todas las asignaciones sólo "se guardan" si esas claves existen... sino, simplemente se ignoran, por tanto no se "crea" el código XML, en la segunda parte veremos cómo crear al vuelo ese código necesario para que el método Save no falle, pero eso será otro día...

 

Usar otras propiedades no "previstas" por el diseñador de formularios y poder leerlas en tiempo de ejecución

Para ir terminando con este artículo sobre appSettings, vamos a ver cómo poder leer otros valores distintos a los que están previstos por el diseñador de Windows.Forms, por ejemplo, podríamos querer almacenar la posición del formulario, de forma que si el usuario cambia la posición, la próxima vez se muestre en esa posición.

Podríamos pensar que si añadimos estas líneas, podría funcionar:

Dim configurationAppSettings As New System.Configuration.AppSettingsReader
Me.Left = CInt(configurationAppSettings.GetValue("Form1.Left", GetType(System.Int32)))

¡Pues si! Estarías en lo cierto, esas líneas funcionan.

Aunque en algunas pruebas que estuve haciendo "pensé" que no funcionaba... así que me fabriqué mi propio método para leer los valores... y... ya que hice el trabajo, te voy a mostrar ese código, entre otras cosas porque en la segunda parte de esta serie de dos artículos lo vamos a usar...

 

Por supuesto, para que funcione el código que te voy a mostrar, deberás añadir las líneas correspondientes al fichero de configuración, y si estás en modo "diseño" te recomendaría que lo hicieras en el app.config que está en el directorio del código, ya que el que está en la carpeta del ejecutable se reemplaza cada vez que compilamos la aplicación.

 

Nuevamente te muestro tanto el código de VB como el de C#, para que elijas el que más te guste.

' El código de Visual Basic
'
Private Function cfgGetValue(ByVal seccion As String, _
                             ByVal clave As String, _
                             ByVal predeterminado As String) As String
    '
    Dim n As XmlNode
    n = configXml.SelectSingleNode(seccion & "/add[@key=""" & clave & """]")
    If Not n Is Nothing Then
        Return n.Attributes("value").InnerText
    Else
        Return predeterminado
    End If
End Function
// El código de C#
//
private string cfgGetValue(string seccion, string clave, string predeterminado) 
{
    //
    XmlNode n;
    n = configXml.SelectSingleNode(seccion + "/add[@key=\"" + clave + "\"]");
    if( n != null )    {
        return n.Attributes["value"].InnerText;
    }
    else {
        return predeterminado;
    }
}

 

En este caso, el método espera tres parámetros, aunque el último que le indicamos es el valor predeterminado, por si un casual esa propiedad no está guardado en el fichero de configuración.

Una vez credo este método, podemos leer otros valores, como por ejemplo los valores Left y Top del formulario:

Me.Left = CInt(cfgGetValue("configuration/appSettings", "Form1.Left", Me.Left.ToString))
Me.Top = CInt(cfgGetValue("configuration/appSettings", "Form1.Top", Me.Top.ToString))


this.Left = Convert.ToInt32(cfgGetValue("configuration/appSettings", "Form1.Left", this.Left.ToString()));
this.Top = Convert.ToInt32(cfgGetValue("configuration/appSettings", "Form1.Top", this.Top.ToString()));

 

(Aquí no te he indicado cual es el código de VB o el de C#, ¿serás capaz de adivinarlo?, je, je, je.)

 

Por supuesto, tendrás que añadir estas líneas al método Closing, para que se guarden al cerrar el formulario, aunque deberías tener la precaución de no guardarlos si el formulario está minimizado, sino... la próxima vez que lo abras no podrás verlo...

'(VB)
If Me.WindowState = FormWindowState.Normal Then
    cfgSetValue("configuration/appSettings", "Form1.Left", Me.Left.ToString)
    cfgSetValue("configuration/appSettings", "Form1.Top", Me.Top.ToString)
End If


//(C#)
if(this.WindowState == FormWindowState.Normal) {
    cfgSetValue("configuration/appSettings", "Form1.Left", this.Left.ToString());
    cfgSetValue("configuration/appSettings", "Form1.Top", this.Top.ToString());
}

 

Pues esto es todo... espero que ahora lo tengas más fácil para manejarte con las propiedades de tus aplicaciones... y que aproveches las "novedades" que te muestro... que en los ejemplos de Visual Studio .NET no he encontrado cómo hacerlo...

En otra ocasión te diré cómo crear otras secciones, además de appSettings, y cómo leer y guardar valores en esas secciones. A lo mejor mañana... que ya es tarde... (por decir algo, ya que con el horario que suelo usar, ahora es tempranísimo... solamente son las nueve de la noche).

Nos vemos.
Guillermo
P.S.
Ah, no hay código para bajar ni nada de eso... creo que con lo aquí mostrado tendrás suficiente para que vayas probando... y si realmente notas que te falta el código completo... mejor lo dejas para cuando sepas un poquillo más.. (que malo es, "algunas veces", el guille)

Nota del 22/Feb/05:
Ya está publicada la segunda parte.

 


Código para Visual Basic.NET (VB.NET)El código para VB .NET

Todo lo que había que mostrar está en el propio artículo.

 


Código para C Sharp (C#)El código para C#

Todo lo que había que mostrar está en el propio artículo.

 


Espacios de nombres usados en este artículo:

System
System.Windows.Forms
System.Xml
System.IO
System.Configuration


la Luna del Guille o... el Guille que está en la Luna... tanto monta...