Configuración de aplicaciones en Visual Studio 2005
En Visual Studio 2005 se introdujo en las propiedades del proyecto
un nuevo apartado, que es precisamente sobre lo que trata este
artículo: la ficha "Settings" ("Configuración" en la versión en
castellano). Por medio de esa ficha podemos definir los datos de
configuración que usará nuestra aplicación. Esos datos de
configuración pueden tener dos niveles, según queramos que sean a
nivel de la aplicación (válida para todos los usuarios) o que esos
datos sean particulares a cada usuario. En el primer caso, los
datos serán de solo lectura; es decir, no podremos modificarlos en
tiempo de ejecución, sino que solo podremos leer los valores
asignados. Por otra parte, los datos de configuración a nivel de
usuario son valores de lectura/escritura, y por tanto los podremos
modificar en tiempo de ejecución, que es lo deseable, ya que así
podremos guardar las preferencias de cada usuario, como por ejemplo
el tamaño y posición de la ventana, algunos de los valores con los
que normalmente trabajará, etc.
La razón de que los valores de configuración a nivel de aplicación
sean de solo lectura es para que el usuario no pueda cambiar un
valor que en realidad es "global" al resto de usuarios; por tanto,
si necesitamos valores "personalizables" por cada usuario, usaremos
el ámbito de usuario. Pero si necesitamos valores modificables a
nivel de toda la aplicación, tendremos que hacerlo manualmente;
Visual Studio no nos proporciona ninguna forma automatizada de que
los valores de configuración con ámbito de aplicación se puedan
modificar en tiempo de ejecución, y por tanto tendremos que usar
nuestra propia forma de acceder y modificar esos valores, ya sea
escribiendo directamente en el propio fichero de configuración de
la aplicación o, mejor aún, creando nuestro propio fichero de
configuración. En el código que acompaña a este artículo incluyo
una clase (ConfigXml) que nos puede servir para realizar esas
configuraciones personalizadas.
Tipos de datos de los valores de configuración
Los tipos de datos que podemos usar en la configuración son muy
variados, e incluyen los tipos "normales" de .NET, y según estemos
usando un lenguaje de programación u otro, esos tipos de datos se
mostrarán según la definición de los mismos en el lenguaje; por
ejemplo, en C# se nos mostrará string, bool o int, mientras que en
Visual Basic será String, Boolean o Integer. También se incluyen
tipos más complejos, como StringCollection o Font, aunque en
realidad podemos usar prácticamente cualquier tipo de datos
definido en .NET Framework e incluso en nuestro propio proyecto; en
un momento veremos cómo.
Añadir valores de configuración a nuestras aplicaciones
Veamos cómo podemos añadir valores de configuración (y usarlos
posteriormente) en nuestras aplicaciones.
Crear valores de configuración
Para crear valores que usaremos para almacenar datos de
configuración debemos pulsar en las propiedades del proyecto ("My
Project" en Visual Basic, "Properties" en C#) y seleccionar la
ficha "Settings". Se nos mostrará algo como lo que aparece en la
figura 1.
Viendo la figura 1 y dejándonos llevar por la intuición, podemos
deducir que en "Nombre" pondremos cómo queremos que se llame ese
valor de configuración, en "Tipo" el tipo de datos que tendrá
(inicialmente siempre se muestra string), en "Ámbito" pondremos si
queremos que sea una valor a nivel de "Usuario" o de "Aplicación" y
en "Valor" el valor inicial que queremos que tenga. Ese valor debe
ser adecuado para el tipo de datos; por ejemplo, en la figura 2
tenemos varios valores de configuración añadidos, entre los que hay
uno de tipo bool, y en ese caso, las opciones que nos muestra son
True y False (con la primera letra en mayúsculas, ya que no son
tipos de C# sino valores que se guardarán como cadenas y después se
convertirán adecuadamente).
Si necesitamos datos más complejos, por ejemplo una colección de
datos, también podemos indicarlo. Tal como vemos en la figura 2, el
valor de configuración "Ficheros" será del tipo StringCollection. Y
en este caso, si queremos asignarle un valor inicial, veremos que
el propio Visual Studio nos muestra una ventana en la que podemos
indicar cada uno de los elementos que la colección tendrá (ver la
figura 3).
Y si lo que necesitamos es algunos de los tipos de datos que no se
han mostrado en la lista desplegable con los tipos a elegir,
podemos seleccionar la última opción ("Examinar"); eso hará que se
muestre una ventana con los tipos de datos que podemos seleccionar,
tal como vemos en la figura 4. Y si el tipo que queremos usar no
está, lo podemos indicar, como es el caso de la clase Colegas que
tenemos en nuestro proyecto.
En este último caso, debemos tener en cuenta que al usar ese tipo
que tenemos definido en nuestro proyecto, se agrega una referencia
a nuestra aplicación; la solución es eliminar esa referencia y
asunto arreglado.
Pero no vamos a complicarnos demasiado, ya que en ese caso no
veremos las cosas importantes de todo esto de la configuración, que
es lo que en realidad importa. Así que veamos ahora cómo usar los
datos de configuración.
Acceder a los datos de configuración
Una vez que hemos definido los valores que queremos usar en nuestra
aplicación, ahora toca poder utilizarlos. En nuestro ejemplo vamos
a usar los valores de configuración que podemos ver en la figura 2.
Lo bueno del sistema de configuración generado por Visual Studio
2005 es que esos valores los usaremos como propiedades de una clase
"especial" que se añade a nuestro proyecto. Esa clase se llama
Settings y en C# está definida en un espacio de nombres llamado
Properties, que su vez está definido dentro del espacio de nombres
de nuestra aplicación. Y debido a que es una clase donde están las
propiedades de los valores de configuración, podríamos pensar que
tendríamos que crear una instancia de esa clase para poder
accederlas; pero esto no es necesario, ya que el propio compilador
de C# define una propiedad estática (compartida) en esa misma clase
que nos permite acceder a esas propiedades que hacen referencia a
los valores de configuración. Esa propiedad se llama Default e
internamente accede a un campo que crea una instancia de la propia
clase. Sabiendo esto, la forma de acceder a esos valores será el
siguiente:
this.Left =
Properties.Settings.Default.Left;
Al definirse como propiedades de una clase, la forma de usarlas es
bastante intuitiva. Y como podemos comprobar, los tipos de datos de
esas propiedades son los mismos que hemos definido en la
configuración; en este ejemplo el tipo "interno" de la propiedad
Left es int, por lo que no tenemos que hacer ningún tipo de
conversión a la hora de asignar el valor.
Si los valores a los que hacen referencia esas propiedades han
cambiado y queremos "persistirlas", lo primero que tenemos que
hacer es asignar el valor, por ejemplo:
Properties.Settings.Default.Left =
this.Left;
Y para que esos datos se guarden tenemos que llamar al método
Save:
Properties.Settings.Default.Save();
Es importante llamar al método Save para asegurarnos de que los
datos se guardan. Y debemos llamarlo siempre que creamos
conveniente, aunque lo recomendable es hacer esa llamada cuando el
formulario se vaya a cerrar, es decir, en el evento
FormClosing.
Sin querer confundir las cosas, me gustaría hacer una aclaración
para los lectores que prefieren usar VB como lenguaje de
programación, y es que en él de forma predeterminada no es
necesario llamar explícitamente al método Save, ya que el
compilador se encarga de añadir la llamada a ese método cuando el
formulario se cierra. Al menos si así lo hemos indicado en las
propiedades del proyecto, marcando la opción "Guardar My.Settings"
al cerrar. Precisamente porque My.Settings es otra funcionalidad
que ofrece Visual Basic para gestionar todo el tema de las
configuraciones, y en realidad consiste en la definición de una
propiedad llamada Settings que está definida en un módulo (y por
tanto no es necesario indicar dónde está definida), y además este
módulo está definido en el espacio de nombres My, por tanto, la
forma de usar los valores de configuración en Visual Basic es la
siguiente:
Me.Left = My.Settings.Left
En el fondo, esa propiedad Settings lo que hace es utilizar la
propiedad "auto-instanciable" Default.
En C# podemos acceder fácilmente a esa propiedad Default sin
necesidad de escribir tanto, definiendo una variable en el
formulario que simplemente haga referencia a esa propiedad
"predeterminada":
Properties.Settings misProp =
Properties.Settings.Default;
De esta forma podremos acceder más fácilmente a las propiedades de
configuración:
misProp.Top = this.Top;
Recibir notificaciones cuando cambien los valores de
configuración
Otra funcionalidad que Visual Studio 2005 pone a nuestra
disposición es la de saber cuándo se va a asignar un valor a una
propiedad de configuración o cuándo se va a guardar o cuándo se han
cargado los valores. Cuando queremos acceder a esos eventos de
configuración, Visual Studio crea una clase parcial con
instrucciones para usar dos de los cuatro eventos disponibles:
SettingChanging y SettingsSaving.
El primero de los eventos anteriores se produce antes de que cambie
uno de los valores de configuración, permitiendo cancelar la
asignación. El segundo se produce antes de que se guarden los
valores, y también podemos cancelarlo, aunque en este caso no
tenemos acceso a los datos que se están guardando salvo que
accedamos a las propiedades de la propia clase; sin embargo, con el
evento que nos informa de los cambios de las propiedades, en la
variable del segundo parámetro se pasa información sobre la
propiedad afectada, el nuevo valor a asignar, etc., lo que veremos
a continuación con más detalle.
Los otros dos eventos son PropertyChanged y SettingsLoaded. En el
primero se nos avisa cuando una propiedad ha cambiado, y la única
información pasada en el segundo parámetro es el nombre de la
propiedad que ha cambiado. El segundo evento se dispara después de
la carga de los valores de configuración.
La forma de acceder a ese fichero de código es desde la ficha
"Configuración" de las propiedades del proyecto y pulsando el botón
"Ver código" que está en la parte superior de esa ficha, tal como
podemos comprobar en la figura 1. Al pulsar ese botón, se abrirá un
fichero con parte del código que normalmente usaremos; en
particular, hay definidos dos de los métodos que interceptarán los
eventos SettingChanging y SettingsSaving, y en el constructor de la
clase están comentadas las instrucciones que ligarán esos métodos
con los eventos. Si queremos interceptar los otros dos eventos,
tendremos que escribir sus "manejadores" por nuestra cuenta; como
ya vimos en el número 31 de dotNetManía, el propio editor de C# nos
ayuda a la creación de los mismos.
Si nos decidimos a interceptar el evento SettingChanging para
detectar los cambios antes de que se asignen a las propiedades,
debemos tener en cuenta que los valores pasados en la variable del
segundo parámetro no son tipos "explícitos"; es decir, si el cambio
se produce en el valor Left, que como vimos es de tipo entero, en
este evento se recibe dicho valor como object, porque ese mismo
evento sirve para todos los valores de configuración, y como hemos
comprobado podemos usar prácticamente cualquier tipo de
datos.
Por tanto, si queremos detectar el cambio en esa propiedad,
tendremos que hacer la conversión correspondiente, tal como vemos
en el siguiente código:
Como vemos, el nombre del valor de configuración lo averiguamos por
medio de SettingName, y el nuevo valor que se va a asignar está en
NewValue. Y si no queremos que ese nuevo valor se asigne,
simplemente asignamos un valor verdadero a la propiedad
Cancel.
Trabajar con colecciones
genéricas personalizadas
Al definir los valores de configuración no podemos utilizar
directamente tipos genéricos, pero nada impide que podamos definir
un tipo de datos propio que esté derivado de un tipo genérico y lo
utilicemos. Por ejemplo, en nuestro proyecto podemos tener creado
un tipo de datos llamado Colega, y podemos querer almacenar
instancias de esa clase en una colección genérica de tipo
List<Colega>. Debido a que Visual Studio no nos permite usar
ese tipo para un valor de configuración, lo que podemos hacer es
definir nuestra clase/colección Colegas de esta forma:
public class Colegas : List<Colega>
{}
Esto simplemente hará que usemos la clase Colegas como una
colección genérica, pudiendo añadir valores de esta forma:
Colegas cols = new Colegas();
Colega c = new Colega("Guille");
cols.Add(c);
cols.Add(new Colega("Pepe"));
Y si hemos definido en la configuración una propiedad que sea del
tipo Colegas que hemos definido en nuestro proyecto, tal como vimos
en la figura 4, podemos asignar valores de este tipo a la
configuración de esta forma:
Properties.Settings.Default.Colegas =
cols;
Y por supuesto, podemos acceder a ese valor de la forma habitual,
además sin necesidad de hacer ningún tipo de conversión, ya que en
las propiedades de la clase Settings siempre se almacenan los datos
usando el tipo que hemos indicado. En el siguiente código obtenemos
los valores de los "colegas" que tengamos en el fichero de
configuración y los asignamos a un control ListBox:
Colegas cols =
Properties.Settings.Default.Colegas;
this.listBox1.Items.AddRange(
cols.ToArray());
Enlace de propiedades con valores de configuración
Algo muy habitual en el manejo de los valores de configuración es
hacer binding de ciertas propiedades de los controles o del
formulario a valores almacenados en la configuración de la
aplicación. Tan habitual es, que es prácticamente casi todo lo que
la documentación de Visual Studio muestra como ejemplos, y "casi"
lo único que se podía hacer en las versiones anteriores de Visual
Studio, aunque a diferencia de éstas, en Visual Studio 2005 esos
valores pueden tener un ámbito de usuario en lugar de ser solo a
nivel de aplicación, con lo cual se facilita su uso.
La forma de ligar cualquier propiedad de cualquier control (o del
formulario) a un valor de configuración es mediante la ventana de
propiedades, en particular de la propiedad "ApplicationSettings",
que en el caso del formulario, mostrará los valores que vemos en la
figura 5.
Como vemos en la figura 5, para el formulario hay dos propiedades
preparadas para recibir el valor. Esas propiedades dependerán del
control que estemos usando, pero si la propiedad que queremos
"persistir" no está en esa lista de propiedades, podemos pulsar en
"PropertyBinding" y desde el cuadro de diálogo que se nos mostrará
podremos seleccionar la propiedad que queremos ligar con un valor
de configuración. Si ya tenemos un valor de configuración del tipo
adecuado, podemos usar esa propiedad o bien crear una nueva; en ese
caso, como vemos en la figura 6, podemos crearla directamente desde
ese mismo cuadro de diálogo.
Al crearla desde este cuadro de diálogo, podemos indicar el ámbito
que queremos que tenga ese valor de configuración además de asignar
los valores que queremos que tenga. Y tal como vemos en la figura
7, en la que asignamos el valor de la propiedad ClientSize, se
mostrará el valor que actualmente tenga la propiedad que queremos
usar.
Una vez que tenemos las propiedades enlazadas a valores de
configuración, en la ventana de propiedades se nos mostrarán todas
las que lo estén, tal como podemos comprobar en la figura 8.
Y en cada una de esas propiedades enlazadas tendremos un icono que
nos indicará esa situación "de enlace", además de indicársenos cuál
es el valor de la configuración con el que la propiedad está
enlazada, tal como vemos en la figura 9.
Ni que decir tiene que es el propio compilador el que se encarga de
implementar todos estos "enlaces", de forma que nosotros no
tengamos que escribir código extra para que se hagan efectivos. Lo
único que tendremos que escribir (si estamos usando C#) es la
llamada al método Save de la clase Settings a la hora de guardar
los datos de configuración, y como ya comenté antes, el mejor sitio
en el que podemos llamar a ese método es en el evento FormClosing,
para que se guarden los valores de configuración cuando el
formulario se esté cerrando y no perdamos nada.
Como truco, decir que hay ciertas propiedades que tienen un
comportamiento no esperado, como es el caso de la propiedad Checked
de los controles RadioButton, ya que el comportamiento esperado de
ese tipo de controles es que al seleccionar un control de un grupo,
el resto se deseleccione, pero al estar la propiedad Checked
enlazada, esa "magia" se pierde, y tendremos que ser nosotros
mismos los que tengamos que asignar los valores adecuados a cada
una de las opciones que estén en un mismo rango. Además de que para
ese tipo de controles, al tener esa propiedad enlazada, el
comportamiento en tiempo de ejecución no es... nada deseable. Por
tanto, mi recomendación es la de no enlazar automáticamente las
propiedades Checked de los controles RadioButton y si lo
necesitamos, hacerlo manualmente, aunque, como es natural, usando
los valores de configuración.
Configuraciones totalmente personalizadas
Esto de que el propio Visual Studio proporcione una forma
automatizada de almacenar valores de configuración está muy bien.
El problema principal es que los valores de configuración a nivel
de aplicación son de solo lectura, es decir, no podemos almacenar
nuevos valores. Por un lado es lógico ese comportamiento, ya que
así un usuario no alterará los valores que todos los usuarios van a
usar. Pero hay casos en los que nos puede interesar que sí se
puedan modificar ciertos valores, por ejemplo, la localización de
una base de datos o de un fichero con cierta información que todos
los usuarios de la aplicación usarán.
En estos casos, podemos actuar de dos formas. Una es creando
nuestro propio sistema de almacenar los datos de configuración; por
ejemplo, yo suelo usar una clase llamada ConfigXml que utilizo en
la mayoría de los casos en los que necesito que los valores de
configuración estén compartidos con todos los usuarios (incluso la
uso para que cada usuario tenga sus propios datos, pero eso más que
nada es por costumbre de usar las cosas que me funcionan y sobre
las que tengo más control).
La otra forma de modificar valores del propio fichero de
configuración es accediendo directamente a dicho fichero, que suele
tener el nombre de la aplicación con la extensión .config y que
suele estar en el directorio del ejecutable (los cambios realizados
en Windows Vista serán en el directorio roaming, pero para nuestro
uso eso no afecta).
Como el fichero de configuración tiene formato XML, lo más fácil es
usar una variable de tipo XmlDocument y sabiendo cómo se almacenan
los datos, nos resultará fácil escribir un método que se encargue
de hacer esas modificaciones por nosotros. En realidad, el acceso
manual solo lo tenemos que hacer para almacenar los valores, ya que
la lectura de esos valores es automática y se realiza al iniciarse
la aplicación.
Veamos cómo podemos modificar esos valores. Lo primero es definir
una variable a nivel del formulario, ya que la usaremos tanto en el
evento FormClosing como desde un método que será el que se encargue
de guardar los datos. Después definimos el método que se encarga de
almacenar los datos en el fichero de configuración y finalmente, en
el evento de cierre del formulario asignamos esos valores. Si no
queremos tener que escribir código extra, esos valores los debemos
tener "ligados" a las propiedades del formulario o de los controles
que nos interese que reflejen los valores de la configuración. Una
observación: si queremos "persistir" la posición del formulario, la
propiedad StartPosition de éste debe tener el valor Manual. En el
listado 1 tenemos todo el código necesario para hacer esto que
acabamos de comentar. Para no alargar demasiado el artículo, en los
comentarios del código se explica el truco para poder acceder
correctamente a los valores de configuración.
Conclusiones
En este artículo hemos visto cómo trabajar con los valores de
configuración en nuestras aplicaciones creadas con Visual C# 2005,
en las que aprovechamos la funcionalidad que nos da el entorno de
desarrollo para automatizar todo el trabajo de asignación y
recuperación de los valores de configuración. Y como no es plan de
olvidarse de los usuarios que utilizan Visual Basic, en el ZIP con
el código he incluido también proyectos creados con Visual Basic
2005 en los que se muestra cómo hacer todo lo comentado en el
artículo, además de extras que por falta de espacio no he podido
explicar, como es la posibilidad de usar la clase ConfigXml o cómo
mostrar y modificar los valores de configuración al estilo de la
ventana de propiedades del propio Visual Studio 2005.