Inicio > .NET Framework, C#, Cómo hacer > Cómo modificar la configuración de un programa

Cómo modificar la configuración de un programa


Una de las cosas interesantes que nos proporciona el .NET Framework es la capacidad de crear y persistir archivos de configuración de forma fácil y rápida. Gracias a esto, ya no tenemos que crear nuestras propias clases para leer archivos INI o archivos del registro de Windows.

Cuando creamos un archivo de configuración y creamos elementos, éstos tienen un alcance: de usuario y de aplicación. Los de aplicación, a diferencia de los de usuario, no se pueden modificar. Lo malo del asunto es que hay configuraciones que necesariamente son de aplicación –como por ejemplo, las conexiones a base de datos. Así pues, surge la duda: ¿cómo cambiarlos programáticamente?

Al final de esta entrada muestro el código de un programa que muestra cómo hacer esto. Dicho programa luce así:

Config

El programa tiene tres partes importantes. La primera, es el archivo de configuración. Dentro de /Properties, se encuentran dos archivos: Settings.settings y Settings.Designer.cs. No hay mucho que decir: ambos archivos los genera el propio Visual C#. Baste decir que Settings.Designer.cs contiene una clase generada automáticamente, llamada Settings, y que deriva de System.Configuration.ApplicationSettingsBase. Esta clase, un singleton, nos permite acceder a los valores de configuración, y cambiar aquellos cuyo alcance sea de usuario (el VC# nos genera los métodos de forma automática).

La segunda parte importante es la clase Config. Ésta hereda de Form, y por lo tanto es una ventana. Contiene un control PropertyGrid al que le decimos que muestre el contenido de Settings.Default. Este control nos permitirá editar aquellas propiedades que sean de lectura y escritura. Si corres el programa, notarás que hay ocho propiedades. Las propiedades "BaseDeDatos", "Password", "Servidor" y "Usuario" son propiedades de lectura y escritura (por ende, con alcance de usuario), y por lo tanto se pueden modificar. Las propiedades "BaseDeDatosApp", "ServidorApp" y "UsuarioApp" son sólo de lectura (y por ende alcance de aplicación), por lo que no se pueden modificar. Finalmente, la propiedad "CnnStr" representa una cadena de conexión a datos y se guarda de forma diferente, además de ser de solo lectura y con alcance de aplicación (y sin posibilidad de poder cambiar este comportamiento). Puedes jugar con estos valores: cuando presiones el botón OK, se guardarán los cambios y saldrás de la aplicación. Al entrar, podrás ver los nuevos valores.

La tercera parte importante está dentro de la clase Config. Notarás que hay una caja de texto que reza: "Connection String:". Si escribes algo ahí, y luego pulsas el botón "Cambiar conexión", se ejecutará un procedimiento para modificar la configuración de dicha propiedad. Este procedimiento es el importante cuando se requiere cambiar valores de configuración cuyo alcance es de usuario. De hecho, lo importante sucede en unas cuántas líneas de código: entre la línea 30 y 35 del archivo Config.cs. Reproduzco esa línea:

Assembly assembly = Assembly.GetExecutingAssembly();
string path = assembly.Location;

Configuration config = ConfigurationManager.OpenExeConfiguration(path);
config.ConnectionStrings.ConnectionStrings["Config.Properties.Settings.CnnStr"].ConnectionString = _cnnstrText.Text;
config.Save(ConfigurationSaveMode.Modified);

Lo que aquí sucede es lo siguiente. Primero, obtenemos la ubicación del ensamblado que está ejecutando el código (esto es, del archivo config.exe). Esto lo hacemos por un problema que se explica adelante. Segundo, obtenemos la configuración para dicho archivo. Tercero, buscamos la sección de conexiones a base de datos (config.ConnectionStrings) y obtenemos la colección de conexiones de datos (config.ConnectionStrings.ConnectionStrings, lo sé, algo raro). En dicha colección buscamos la que corresponde a nuestra propiedad de configuración (en este caso,config.ConnectionStrings.ConnectionStrings["Config.Properties.Settings.CnnStr"] y establecemos la propiedad ConnectionString con el valor deseado. Por último, guardamos los cambios.

La clave de todo esto, por supuesto, es la llamada a ConfigurationManager.OpenExeConfiguration. A partir de ahí, solo hay que navegar entre las secciones. En este ejemplo, queríamos cambiar la conexión a la base de datos, pero lo mismo se puede hacer para cualquier otra sección. Ya dependerá de ti.

Ya por último, mencionar que si hacemos ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) en lugar de como lo hicimos, tendremos un desagradable problema al querer ejecutar el código desde el Visual C#. Esto es debido a que el VC# genera, aparte del ejecutable en cuestión, otro ejecutable llamado vshost (usualmente [TuApp].vshost.exe), desde el cuál se lanza la depuración del programa. Al iniciarse el programa, pues, la aplicación genera el archivo [TuApp].exe.config, donde guarda las configuraciones por default. Pero como se ejecuta desde el vshost, entonces se creará el archivo [TuApp].vshost.exe.config, y ahí es donde la puerca tuerce el rabo, porque el código de ejemplo modificará a [TuApp].exe.config. Pero tú seguirás viendo [TuApp].vshost.exe.config. Por el contrario, si vas a tu carpeta Debug y haces doble clic sobre [TuApp].exe, todo funcionará a las mil maravillas. Así las cosas, la forma en la que llamamos a OpenExeConfiguration le da la vuelta a este problema.

Bueno, pues eso es todo.

Código

Config.cs
using System;
using System.Reflection;
using System.Configuration;
using System.Windows.Forms;
using Config.Properties;

namespace Config
{
    public partial class Config : Form
    {
        public Config()
        {
            InitializeComponent();
        }

        private void Config_Load(object sender, EventArgs e)
        {
            _configGrid.SelectedObject = Settings.Default;
            _cnnstrText.Text = Settings.Default.CnnStr;
        }

        private void _okButton_Click(object sender, EventArgs e)
        {
            Settings.Default.Save();
            Close();
        }

        private void _cnnstrButton_Click(object sender, EventArgs e)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string path = assembly.Location;
            
            Configuration config = ConfigurationManager.OpenExeConfiguration(path);
            config.ConnectionStrings.ConnectionStrings["Config.Properties.Settings.CnnStr"].ConnectionString = _cnnstrText.Text;
            config.Save(ConfigurationSaveMode.Modified);

            DialogResult result;
            result = MessageBox.Show("Los cambios se harán patentes al reiniciar el programa. ¿Desea reiniciarlo ahora?", Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result == DialogResult.Yes)
                Application.Restart();

        }
    }
}

Config.designer.cs
namespace Config
{
    partial class Config
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this._configGrid = new System.Windows.Forms.PropertyGrid();
            this._tituloLabel = new System.Windows.Forms.Label();
            this._okButton = new System.Windows.Forms.Button();
            this._cnnstrLabel = new System.Windows.Forms.Label();
            this._cnnstrText = new System.Windows.Forms.TextBox();
            this._cnnstrButton = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // _configGrid
            // 
            this._configGrid.Location = new System.Drawing.Point(12, 25);
            this._configGrid.Name = "_configGrid";
            this._configGrid.Size = new System.Drawing.Size(318, 258);
            this._configGrid.TabIndex = 0;
            // 
            // _tituloLabel
            // 
            this._tituloLabel.AutoSize = true;
            this._tituloLabel.Location = new System.Drawing.Point(12, 9);
            this._tituloLabel.Name = "_tituloLabel";
            this._tituloLabel.Size = new System.Drawing.Size(305, 13);
            this._tituloLabel.TabIndex = 1;
            this._tituloLabel.Text = "A continuación se muestra las propiedades del objeto Settings. ";
            // 
            // _okButton
            // 
            this._okButton.Location = new System.Drawing.Point(255, 333);
            this._okButton.Name = "_okButton";
            this._okButton.Size = new System.Drawing.Size(75, 23);
            this._okButton.TabIndex = 2;
            this._okButton.Text = "OK";
            this._okButton.UseVisualStyleBackColor = true;
            this._okButton.Click += new System.EventHandler(this._okButton_Click);
            // 
            // _cnnstrLabel
            // 
            this._cnnstrLabel.AutoSize = true;
            this._cnnstrLabel.Location = new System.Drawing.Point(12, 286);
            this._cnnstrLabel.Name = "_cnnstrLabel";
            this._cnnstrLabel.Size = new System.Drawing.Size(94, 13);
            this._cnnstrLabel.TabIndex = 3;
            this._cnnstrLabel.Text = "Connection String:";
            // 
            // _cnnstrText
            // 
            this._cnnstrText.Location = new System.Drawing.Point(15, 302);
            this._cnnstrText.Name = "_cnnstrText";
            this._cnnstrText.Size = new System.Drawing.Size(315, 20);
            this._cnnstrText.TabIndex = 4;
            // 
            // _cnnstrButton
            // 
            this._cnnstrButton.Location = new System.Drawing.Point(142, 333);
            this._cnnstrButton.Name = "_cnnstrButton";
            this._cnnstrButton.Size = new System.Drawing.Size(107, 23);
            this._cnnstrButton.TabIndex = 5;
            this._cnnstrButton.Text = "Cambiar conexión";
            this._cnnstrButton.UseVisualStyleBackColor = true;
            this._cnnstrButton.Click += new System.EventHandler(this._cnnstrButton_Click);
            // 
            // Config
            // 
            this.AcceptButton = this._okButton;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(342, 368);
            this.Controls.Add(this._cnnstrButton);
            this.Controls.Add(this._cnnstrText);
            this.Controls.Add(this._cnnstrLabel);
            this.Controls.Add(this._okButton);
            this.Controls.Add(this._tituloLabel);
            this.Controls.Add(this._configGrid);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "Config";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "Configuración";
            this.Load += new System.EventHandler(this.Config_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.PropertyGrid _configGrid;
        private System.Windows.Forms.Label _tituloLabel;
        private System.Windows.Forms.Button _okButton;
        private System.Windows.Forms.Label _cnnstrLabel;
        private System.Windows.Forms.TextBox _cnnstrText;
        private System.Windows.Forms.Button _cnnstrButton;
    }
}


Program.cs
using System;
using System.Windows.Forms;

namespace Config
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Properties.Settings.Default.Save();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Config());
        }
    }
}

Settings.designer.cs
namespace Config.Properties 
{    
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 
	{
        private static Settings defaultInstance = new Settings(); 
        
        public static Settings Default {
            get { return defaultInstance; }
        }
        
        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("")]
        public string Usuario {
            get { return ((string)(this["Usuario"])); }
            set { this["Usuario"] = value; }
        }
        
        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("")]
        public string Password {
            get {
                return ((string)(this["Password"]));
            }
            set {
                this["Password"] = value;
            }
        }
        
        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("")]
        public string BaseDeDatos {
            get { return ((string)(this["BaseDeDatos"])); }
            set { this["BaseDeDatos"] = value; }
        }
        
        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("")]
        public string Servidor {
            get { return ((string)(this["Servidor"])); }
            set { this["Servidor"] = value; }
        }
        
        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("fgomez")]
        public string UsuarioApp {
            get { return ((string)(this["UsuarioApp"])); }
        }
        
        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("midb")]
        public string BaseDeDatosApp {
            get { return ((string)(this["BaseDeDatosApp"])); }
        }
        
        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("192.168.0.28")]
        public string ServidorApp {
            get { return ((string)(this["ServidorApp"])); }
        }
        
        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
        [global::System.Configuration.DefaultSettingValueAttribute("server=epeople28; database=sternpluspos; user=fgomez")]
        public string CnnStr {
            get { return ((string)(this["CnnStr"])); }
        }
    }
}

Settings.settings
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Config.Properties" GeneratedClassName="Settings">
  <Profiles />
  <Settings>
    <Setting Name="Usuario" Type="System.String" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
    <Setting Name="Password" Type="System.String" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
    <Setting Name="BaseDeDatos" Type="System.String" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
    <Setting Name="Servidor" Type="System.String" Scope="User">
      <Value Profile="(Default)" />
    </Setting>
    <Setting Name="UsuarioApp" Type="System.String" Scope="Application">
      <Value Profile="(Default)">fgomez</Value>
    </Setting>
    <Setting Name="BaseDeDatosApp" Type="System.String" Scope="Application">
      <Value Profile="(Default)">midb</Value>
    </Setting>
    <Setting Name="ServidorApp" Type="System.String" Scope="Application">
      <Value Profile="(Default)">192.168.0.28</Value>
    </Setting>
    <Setting Name="CnnStr" Type="(Connection string)" Scope="Application">
      <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  &lt;ConnectionString&gt;server=epeople28; database=sternpluspos; user=fgomez&lt;/ConnectionString&gt;
&lt;/SerializableConnectionString&gt;</DesignTimeValue>
      <Value Profile="(Default)">server=epeople28; database=sternpluspos; user=fgomez</Value>
    </Setting>
  </Settings>
</SettingsFile>
app.settings
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="Config.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
        </sectionGroup>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="Config.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <connectionStrings>
        <add name="Config.Properties.Settings.CnnStr" connectionString="server=epeople28; database=sternpluspos; user=fgomez" />
    </connectionStrings>
    <userSettings>
        <Config.Properties.Settings>
            <setting name="Usuario" serializeAs="String">
                <value />
            </setting>
            <setting name="Password" serializeAs="String">
                <value />
            </setting>
            <setting name="BaseDeDatos" serializeAs="String">
                <value />
            </setting>
            <setting name="Servidor" serializeAs="String">
                <value />
            </setting>
        </Config.Properties.Settings>
    </userSettings>
    <applicationSettings>
        <Config.Properties.Settings>
            <setting name="UsuarioApp" serializeAs="String">
                <value>fgomez</value>
            </setting>
            <setting name="BaseDeDatosApp" serializeAs="String">
                <value>midb</value>
            </setting>
            <setting name="ServidorApp" serializeAs="String">
                <value>192.168.0.28</value>
            </setting>
        </Config.Properties.Settings>
    </applicationSettings>
</configuration>
Categorías: .NET Framework, C#, Cómo hacer Etiquetas:
  1. juan
    octubre 6, 2014 a las 11:01 am

    donde encuentro ese xml que se llama app.settings ? gracias

  2. juan
    noviembre 12, 2014 a las 12:04 pm

    Y si trato hacer el cambio de una cadena, desde otra capa, por ejemplo desde presentación hacer el cambio de la cadena de conexión de la capa de negocio, pero cuando lo intento hacer me marca una excepcion tipo null reference exception o referencia no establecida a objeto…..ojala me puedas dar una opinion

  1. enero 2, 2011 a las 12:12 pm

Deja un comentario