Prácticas recomendadas para guardar la configuración de la aplicación en una aplicación de Windows Forms [cerrado]


Lo que quiero lograr es muy simple: tengo una aplicación de Windows Forms (. NET 3.5) que usa una ruta para leer información. Esta ruta puede ser modificada por el usuario, utilizando el formulario de opciones que proporciono.

Ahora, quiero guardar el valor de la ruta en un archivo para su uso posterior. Esta sería una de las muchas configuraciones guardadas en este archivo. Este archivo se ubicaría directamente en la carpeta de la aplicación.

Entiendo que hay tres opciones disponibles:

  • Archivo ConfigurationSettings (appname.exe.config)
  • Registro
  • Archivo XML personalizado

He leído que el archivo de configuración.NET no está previsto para guardar valores en él. En cuanto a la secretaría, me gustaría alejarme lo más posible de ella.

¿Significa esto que debería usar un archivo XML personalizado para guardar los ajustes de configuración? Si es así, me gustaría ver un ejemplo de código de eso (C#).

He visto otras discusiones sobre este tema, pero todavía no está claro para mí.

Author: Răzvan Flavius Panda, 2009-01-17

13 answers

Si trabaja con Visual Studio, es bastante fácil obtener configuraciones persistentes. Haga clic derecho en el proyecto en el Explorador de soluciones, elija Propiedades. Seleccione la pestaña Configuración, haga clic en el hipervínculo si la configuración no existe. Utilice la ficha Configuración para crear la configuración de la aplicación. Visual Studio crea los archivos Settings.settings y Settings.Designer.settings que contienen la clase singleton Settings heredada de ApplicationSettingsBase. Puede acceder a esta clase desde su código para leer / escribir la aplicación valores:

Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file

Esta técnica es aplicable tanto para consolas, Formularios Windows y otros tipos de proyectos.

Tenga en cuenta que debe establecer la propiedad scope de su configuración. Si selecciona Ámbito de aplicación, entonces Configuración.Predeterminado. será de solo lectura.

 538
Author: aku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-06 06:15:32

Si está planeando guardar en un archivo dentro del mismo directorio que su ejecutable, aquí hay una buena solución que utiliza el formato JSON :

using System;
using System.IO;
using System.Web.Script.Serialization;

namespace MiscConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            MySettings settings = MySettings.Load();
            Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
            Console.WriteLine("Incrementing 'myInteger'...");
            settings.myInteger++;
            Console.WriteLine("Saving settings...");
            settings.Save();
            Console.WriteLine("Done.");
            Console.ReadKey();
        }

        class MySettings : AppSettings<MySettings>
        {
            public string myString = "Hello World";
            public int myInteger = 1;
        }
    }

    public class AppSettings<T> where T : new()
    {
        private const string DEFAULT_FILENAME = "settings.json";

        public void Save(string fileName = DEFAULT_FILENAME)
        {
            File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
        }

        public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
        {
            File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
        }

        public static T Load(string fileName = DEFAULT_FILENAME)
        {
            T t = new T();
            if(File.Exists(fileName))
                t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
            return t;
        }
    }
}
 79
Author: Trevor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-02 22:33:07

El registro no funciona. No está seguro de si el usuario que usa su aplicación tiene suficientes derechos para escribir en el registro.

Puede usar el archivo app.config para guardar la configuración de nivel de aplicación (que es la misma para cada usuario que usa su aplicación).

Almacenaría la configuración específica del usuario en un archivo XML, que se guardaría en Almacenamiento aislado o en la carpeta especial .ApplicationData directorio.

Junto a eso, a partir de. NET 2.0, se es posible almacenar valores en el archivo app.config.

 63
Author: Frederik Gheysels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-07 08:33:07

La clase ApplicationSettings no admite guardar configuraciones en la aplicación.archivo de configuración. Eso es mucho por diseño, las aplicaciones que se ejecutan con una cuenta de usuario correctamente protegida (piense Vista UAC) no tienen acceso de escritura a la carpeta de instalación del programa.

Puedes luchar contra el sistema con la clase ConfigurationManager. Pero la solución trivial es ir al diseñador de configuraciones y cambiar el alcance de la configuración a Usuario. Si eso causa dificultades (por ejemplo, la configuración es relevante para cada usuario), debe poner su Las opciones se presentan en un programa separado para que pueda solicitar la solicitud de elevación de privilegios. O prescindir de usar una configuración.

 18
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-12 18:35:25

Quería compartir una biblioteca que he construido para esto. Es una pequeña biblioteca, pero una gran mejora (en mi humilde opinión).archivos de configuración.

La biblioteca se llama Jot (GitHub), aquí hay un viejo El artículo del Proyecto Code que escribí sobre él.

Así es como lo usarías para hacer un seguimiento del tamaño y la ubicación de una ventana:

public MainWindow()
{
    InitializeComponent();

    _stateTracker.Configure(this)
        .IdentifyAs("MyMainWindow")
        .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
        .RegisterPersistTrigger(nameof(Closed))
        .Apply();
}

El beneficio en comparación con .archivos de configuración: Hay mucho menos código, y es mucho menos propenso a errores, ya que solo necesita mencione cada propiedad una vez.

Con los archivos de configuración debe mencionar cada propiedad cinco veces: una vez cuando crea explícitamente la propiedad y otras cuatro veces en el código que copia los valores de un lado a otro.

Almacenamiento, serialización, etc. son completamente configurables. Al usar inversión de control , puede conectarlo para que aplique el seguimiento automáticamente a todos los objetos que resuelve para que todo lo que necesita hacer para hacer un propiedad persistente es slap un atributo [Rastreable] en él.

Estoy escribiendo todo esto, porque creo que la biblioteca es de primera categoría, y me gustaría popularizarla:)

 13
Author: anakic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-15 16:59:16

El argumento registry/ConfigurationSettings/XML todavía parece muy activo. Los he usado todos, a medida que la tecnología ha progresado, pero mi favorito se basa en el sistema de Threed combinado con Almacenamiento aislado.

El siguiente ejemplo permite el almacenamiento de un objeto llamado properties en un archivo en almacenamiento aislado. Tales como:

AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn");

Las propiedades se pueden recuperar usando:

AppSettings.Load(myobject, "myFile.jsn");

Es solo una muestra, no sugiere las mejores prácticas.

internal static class AppSettings
{
    internal static void Save(object src, string targ, string fileName)
    {
        Dictionary<string, object> items = new Dictionary<string, object>();
        Type type = src.GetType();

        string[] paramList = targ.Split(new char[] { ',' });
        foreach (string paramName in paramList)
            items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null));

        try
        {
            // GetUserStoreForApplication doesn't work - can't identify.
            // application unless published by ClickOnce or Silverlight
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write((new JavaScriptSerializer()).Serialize(items));
            }

        }
        catch (Exception) { }   // If fails - just don't use preferences
    }

    internal static void Load(object tar, string fileName)
    {
        Dictionary<string, object> items = new Dictionary<string, object>();
        Type type = tar.GetType();

        try
        {
            // GetUserStoreForApplication doesn't work - can't identify
            // application unless published by ClickOnce or Silverlight
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
            using (StreamReader reader = new StreamReader(stream))
            {
                items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd());
            }
        }
        catch (Exception) { return; }   // If fails - just don't use preferences.

        foreach (KeyValuePair<string, object> obj in items)
        {
            try
            {
                tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null);
            }
            catch (Exception) { }
        }
    }
}
 12
Author: Boczek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:47

Una forma sencilla es usar un objeto de datos de configuración, guardarlo como un archivo XML con el nombre de la aplicación en la carpeta local y al iniciar leerlo de nuevo.

Aquí hay un ejemplo para almacenar la posición y el tamaño de un formulario.

El objeto de datos de configuración está fuertemente escrito y es fácil de usar:

[Serializable()]
public class CConfigDO
{
    private System.Drawing.Point m_oStartPos;
    private System.Drawing.Size m_oStartSize;

    public System.Drawing.Point StartPos
    {
        get { return m_oStartPos; }
        set { m_oStartPos = value; }
    }

    public System.Drawing.Size StartSize
    {
        get { return m_oStartSize; }
        set { m_oStartSize = value; }
    }
}

Una clase manager para guardar y cargar:

public class CConfigMng
{
    private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml";
    private CConfigDO m_oConfig = new CConfigDO();

    public CConfigDO Config
    {
        get { return m_oConfig; }
        set { m_oConfig = value; }
    }

    // Load configuration file
    public void LoadConfig()
    {
        if (System.IO.File.Exists(m_sConfigFileName))
        {
            System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName);
            Type tType = m_oConfig.GetType();
            System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
            object oData = xsSerializer.Deserialize(srReader);
            m_oConfig = (CConfigDO)oData;
            srReader.Close();
        }
    }

    // Save configuration file
    public void SaveConfig()
    {
        System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName);
        Type tType = m_oConfig.GetType();
        if (tType.IsSerializable)
        {
            System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
            xsSerializer.Serialize(swWriter, m_oConfig);
            swWriter.Close();
        }
    }
}

Ahora puedes crear una instancia y usarla en los eventos load y close de tu formulario:

    private CConfigMng oConfigMng = new CConfigMng();

    private void Form1_Load(object sender, EventArgs e)
    {
        // Load configuration
        oConfigMng.LoadConfig();
        if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0)
        {
            Location = oConfigMng.Config.StartPos;
            Size = oConfigMng.Config.StartSize;
        }
    }

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Save configuration
        oConfigMng.Config.StartPos = Location;
        oConfigMng.Config.StartSize = Size;
        oConfigMng.SaveConfig();
    }

Y el producido El archivo XML también es legible:

<?xml version="1.0" encoding="utf-8"?>
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <StartPos>
    <X>70</X>
    <Y>278</Y>
  </StartPos>
  <StartSize>
    <Width>253</Width>
    <Height>229</Height>
  </StartSize>
</CConfigDO>
 9
Author: Dieter Meemken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-25 13:30:14

No me gusta la solución propuesta de usar web.config o app.config. Intenta leer tu propio XML. Echa un vistazo a Archivos de configuración XML-No más web.config.

 6
Author: gatapia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-28 00:41:28

Por lo que puedo decir,. NET admite la configuración persistente utilizando la función de configuración de la aplicación integrada:

La función Configuración de la aplicación de Formularios de Windows facilita la creación, el almacenamiento y el mantenimiento de preferencias personalizadas de aplicaciones y usuarios en el equipo cliente. Con la configuración de la aplicación de Windows Forms, puede almacenar no solo los datos de la aplicación, como las cadenas de conexión a la base de datos, sino también los datos específicos del usuario, como las preferencias de la aplicación del usuario. Usando Visual Studio o código administrado personalizado, puede crear nuevas configuraciones, leerlas y escribirlas en el disco, vincularlas a propiedades en sus formularios y validar los datos de configuración antes de cargarlas y guardarlas. - http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

 2
Author: Jacob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-24 23:26:17

Otras opciones, en lugar de usar un archivo XML personalizado, podemos usar un formato de archivo más fácil de usar: archivo JSON o YAML.

  • Si utiliza. NET 4.0 dynamic, esta biblioteca es realmente fácil de usar (serializar, deserializar, soporte de objetos anidados y ordenar la salida como desee + fusionar múltiples configuraciones en una) JsonConfig (el uso es equivalente a ApplicationSettingsBase)
  • Para la biblioteca de configuración YAML.NET... No he encontrado uno que sea tan fácil de usar como JsonConfig

Puede almacenar su archivo de configuración en varias carpetas especiales (para todos los usuarios y por usuario) como se indica aquí Entorno.Enumeración de carpeta especial y varios archivos (por defecto solo lectura, por rol, por usuario, etc.)

Si elige usar varias configuraciones, puede fusionarlas: Por ejemplo, fusionar configuraciones para default + BasicUser + AdminUser. Puedes usar tus propias reglas: la última anula el valor, etc.

 2
Author: kite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

A veces desea deshacerse de los ajustes mantenidos en la web tradicional.configuración o aplicación.archivo de configuración. Desea un control más preciso sobre la implementación de las entradas de configuración y el diseño de datos separados. O bien, el requisito es habilitar la adición de nuevas entradas en tiempo de ejecución.

Puedo imaginar dos buenas opciones:

  • La versión fuertemente mecanografiada y
  • La versión orientada a objetos.

La ventaja de la versión fuertemente mecanografiada es la fuertemente mecanografiada nombres y valores de configuración. No hay riesgo de mezclar nombres o tipos de datos. La desventaja es que se tienen que codificar más configuraciones, no se pueden agregar en tiempo de ejecución.

Con la versión orientada a objetos la ventaja es que se pueden agregar nuevas configuraciones en tiempo de ejecución. Pero no tiene nombres y valores fuertemente tipeados. Debe tener cuidado con los identificadores de cadena. Debe conocer el tipo de datos guardado antes al obtener un valor.

Puede encontrar el código de ambas implementaciones completamente funcionales AQUÍ .

 1
Author: user3130351,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-27 18:35:03

" ¿Significa esto que debería usar un archivo XML personalizado para guardar los ajustes de configuración?"No, no necesariamente. Utilizamos SharpConfig para tales operaciones.

Por ejemplo, si el archivo de configuración es así

[General]
# a comment
SomeString = Hello World!
SomeInteger = 10 # an inline comment

Podemos recuperar valores como este

var config = Configuration.LoadFromFile("sample.cfg");
var section = config["General"];

string someString = section["SomeString"].StringValue;
int someInteger = section["SomeInteger"].IntValue;

Es compatible con.Net 2.0 y versiones posteriores. Podemos crear archivos de configuración sobre la marcha y podemos guardarlos más tarde. Fuente: http://sharpconfig.net / Github: https://github.com/cemdervis/SharpConfig

Espero que ayude.

 1
Author: Turker Tunali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-14 20:07:28
public static class SettingsExtensions
{
    public static bool TryGetValue<T>(this Settings settings, string key, out T value)
    {
        if (settings.Properties[key] != null)
        {
            value = (T) settings[key];
            return true;
        }

        value = default(T);
        return false;
    }

    public static bool ContainsKey(this Settings settings, string key)
    {
        return settings.Properties[key] != null;
    }

    public static void SetValue<T>(this Settings settings, string key, T value)
    {
        if (settings.Properties[key] == null)
        {
            var p = new SettingsProperty(key)
            {
                PropertyType = typeof(T),
                Provider = settings.Providers["LocalFileSettingsProvider"],
                SerializeAs = SettingsSerializeAs.Xml
            };
            p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
            var v = new SettingsPropertyValue(p);
            settings.Properties.Add(p);
            settings.Reload();
        }
        settings[key] = value;
        settings.Save();
    }
}
 0
Author: Paul - Soura Tech LLC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-27 16:44:11