c # - ¿enfoque para guardar la configuración del usuario en una aplicación WPF?

84

¿Qué enfoque recomienda para conservar la configuración del usuario en una aplicación de WPF para Windows (escritorio)? Tenga en cuenta que la idea es que el usuario puede cambiar su configuración en tiempo de ejecución, y luego puede cerrar la aplicación, luego, al iniciar la aplicación más tarde, la aplicación utilizará la configuración actual. Efectivamente, entonces parecerá que la configuración de la aplicación no cambia.

Q1 - ¿Base de datos u otro enfoque? Tengo una base de datos sqlite que usaré de todos modos, por lo tanto, ¿usar una tabla en la base de datos sería tan bueno como cualquier enfoque?

P2 - If Database: ¿Qué diseño de tabla de base de datos? Una tabla con columnas para diferentes tipos de datos que se pueda tener (por ejemplo string, long, DateTimeetc) o simplemente una mesa con una cadena para el valor sobre el cual usted tiene que serializar y deserializar los valores? Creo que la primera sería más fácil, y si no hay muchas configuraciones, ¿la sobrecarga no es mucha?

P3 - ¿Se puede utilizar la configuración de la aplicación para esto? Si es así, ¿se requieren tareas especiales para habilitar la persistencia aquí? Además, ¿qué pasaría con respecto al uso del valor "predeterminado" en el diseñador de configuración de la aplicación en este caso? ¿El valor predeterminado anularía cualquier configuración que se guardó entre la ejecución de la aplicación? (o necesitaría NO usar el valor predeterminado)

Greg
fuente
@Todos los nuevos usuarios Se prefiere si puede publicar preguntas por separado en lugar de combinar sus preguntas en una sola. De esa manera, ayuda a las personas que responden a su pregunta y también a otras personas que buscan al menos una de sus preguntas. ¡Gracias!
Hille

Respuestas:

22

Actualización : hoy en día usaría JSON.

También prefiero ir con la serialización al archivo. Los archivos XML se adaptan principalmente a todos los requisitos. Puede usar la ApplicationSettingscompilación, pero tienen algunas restricciones y un comportamiento definido pero (para mí) muy extraño donde se almacenan. Los usé mucho y funcionan. Pero si quieres tener un control total sobre cómo y dónde se almacenan, utilizo otro enfoque.

  1. Haz una clase en algún lugar con todas tus configuraciones. Lo nombréMySettings
  2. Implementar Guardar y leer para persistencia
  3. Úselos en su código de aplicación

Ventajas:

  • Enfoque muy simple.
  • Una clase para entornos. Carga. Salvar.
  • Todas sus configuraciones son seguras de escritura.
  • Puede simplificar o ampliar la lógica a sus necesidades (control de versiones, muchos perfiles por usuario, etc.)
  • Funciona muy bien en cualquier caso (Base de datos, WinForms, WPF, Servicio, etc ...)
  • Puede definir dónde almacenar los archivos XML.
  • Puede encontrarlos y manipularlos por código o manual
  • Funciona para cualquier método de implementación que pueda imaginar.

Desventajas: - Tienes que pensar en dónde almacenar tus archivos de configuración. (Pero puede usar su carpeta de instalación)

Aquí hay un ejemplo simple (no probado):

public class MySettings
{
    public string Setting1 { get; set; }
    public List<string> Setting2 { get; set; }

    public void Save(string filename)
    {
        using (StreamWriter sw = new StreamWriter(filename))
        {
            XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
            xmls.Serialize(sw, this);
        }
    }
    public MySettings Read(string filename)
    {
        using (StreamReader sw = new StreamReader(filename))
        {
            XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
            return xmls.Deserialize(sw) as MySettings;
        }
    }
}

Y así es como se usa. Es posible cargar valores predeterminados o anularlos con la configuración del usuario simplemente verificando si existe la configuración del usuario:

public class MyApplicationLogic
{
    public const string UserSettingsFilename = "settings.xml";
    public string _DefaultSettingspath = 
        Assembly.GetEntryAssembly().Location + 
        "\\Settings\\" + UserSettingsFilename;

    public string _UserSettingsPath = 
        Assembly.GetEntryAssembly().Location + 
        "\\Settings\\UserSettings\\" + 
        UserSettingsFilename;

    public MyApplicationLogic()
    {
        // if default settings exist
        if (File.Exists(_UserSettingsPath))
            this.Settings = Settings.Read(_UserSettingsPath);
        else
            this.Settings = Settings.Read(_DefaultSettingspath);
    }
    public MySettings Settings { get; private set; }

    public void SaveUserSettings()
    {
        Settings.Save(_UserSettingsPath);
    }
}

tal vez alguien se sienta inspirado por este enfoque. Así es como lo hago ahora durante muchos años y estoy bastante contento con eso.

Estera
fuente
1
Para la desventaja, sería que ya no tiene el diseñador de configuraciones, por lo que es un poco menos fácil de usar cuando ambos funcionan.
Phil1970
3
Totalmente de acuerdo con el "comportamiento muy extraño donde se almacenan", estoy usando su enfoque precisamente por esto. +1.
Hannish
Si tiene una pregunta NUEVA, hágala haciendo clic en el botón Preguntar .
Mat
12

Puede almacenar su información de configuración a partir Stringsde XML en el archivo Settings.Default. Cree algunas clases para almacenar sus datos de configuración y asegúrese de que lo estén [Serializable]. Luego, con los siguientes ayudantes, puede serializar instancias de estos objetos, o List<T>(o matrices T[], etc.) de ellos, en String. Almacene cada una de estas diversas cadenas en su propia Settings.Defaultranura respectiva en su aplicación WPF Settings.

Para recuperar los objetos la próxima vez que se inicie la aplicación, lea la Settingscadena de interés y Deserializeel tipo esperado T(que esta vez debe especificarse explícitamente como un argumento de tipo Deserialize<T>).

public static String Serialize<T>(T t)
{
    using (StringWriter sw = new StringWriter())
    using (XmlWriter xw = XmlWriter.Create(sw))
    {
        new XmlSerializer(typeof(T)).Serialize(xw, t);
        return sw.GetStringBuilder().ToString();
    }
}

public static T Deserialize<T>(String s_xml)
{
    using (XmlReader xw = XmlReader.Create(new StringReader(s_xml)))
        return (T)new XmlSerializer(typeof(T)).Deserialize(xw);
}
Glenn Slayden
fuente
6

El enfoque más típico a largo plazo para esta pregunta es: Almacenamiento aislado.

Serialice su estado de control en XML o algún otro formato (especialmente fácilmente si está guardando Propiedades de dependencia con WPF), luego guarde el archivo en el almacenamiento aislado del usuario.

Si desea seguir la ruta de configuración de la aplicación, probé algo similar en un punto yo mismo ... aunque el enfoque a continuación podría adaptarse fácilmente para usar Almacenamiento aislado:

class SettingsManager
{
    public static void LoadSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        EnsureProperties(sender, savedElements);
        foreach (FrameworkElement element in savedElements.Keys)
        {
            try
            {
                element.SetValue(savedElements[element], Properties.Settings.Default[sender.Name + "." + element.Name]);
            }
            catch (Exception ex) { }
        }
    }

    public static void SaveSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        EnsureProperties(sender, savedElements);
        foreach (FrameworkElement element in savedElements.Keys)
        {
            Properties.Settings.Default[sender.Name + "." + element.Name] = element.GetValue(savedElements[element]);
        }
        Properties.Settings.Default.Save();
    }

    public static void EnsureProperties(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        foreach (FrameworkElement element in savedElements.Keys)
        {
            bool hasProperty =
                Properties.Settings.Default.Properties[sender.Name + "." + element.Name] != null;

            if (!hasProperty)
            {
                SettingsAttributeDictionary attributes = new SettingsAttributeDictionary();
                UserScopedSettingAttribute attribute = new UserScopedSettingAttribute();
                attributes.Add(attribute.GetType(), attribute);

                SettingsProperty property = new SettingsProperty(sender.Name + "." + element.Name,
                    savedElements[element].DefaultMetadata.DefaultValue.GetType(), Properties.Settings.Default.Providers["LocalFileSettingsProvider"], false, null, SettingsSerializeAs.String, attributes, true, true);
                Properties.Settings.Default.Properties.Add(property);
            }
        }
        Properties.Settings.Default.Reload();
    }
}

.....y....

  Dictionary<FrameworkElement, DependencyProperty> savedElements = new Dictionary<FrameworkElement, DependencyProperty>();

public Window_Load(object sender, EventArgs e) {
           savedElements.Add(firstNameText, TextBox.TextProperty);
                savedElements.Add(lastNameText, TextBox.TextProperty);

            SettingsManager.LoadSettings(this, savedElements);
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SettingsManager.SaveSettings(this, savedElements);
        }
Jeff
fuente
5

Además de una base de datos, también puede tener las siguientes opciones para guardar la configuración relacionada con el usuario

  1. registro bajo HKEY_CURRENT_USER

  2. en un archivo en AppDatacarpeta

  3. usando el Settingsarchivo en WPF y estableciendo su alcance como Usuario

ajay_whiz
fuente
2
La sugerencia 1 es la razón por la que las aplicaciones ralentizan Windows, es mejor no llenar las claves de registro con algo mejor hecho en un archivo, en mi opinión.
Consola
1
@Console, escribir un archivo en un disco ralentiza (desgasta) SSD, escribir datos en la base de datos ralentiza la base de datos. Entonces, ¿cuál es tu opción? El registro de Windows está destinado a utilizarse como uno de los lugares para guardar la configuración .
Sinatr
1
Tienes razón, creo que es importante mencionar que el registro tiene algunos inconvenientes si cada aplicación guarda cientos de preferencias de usuario allí.
Consola
@Sinatr Originalmente, el registro estaba destinado a ese propósito ... pero no fue diseñado para manejar una gran cantidad de datos, por lo que en algún momento de la historia, Microsoft recomendó dejar de usarlo. Hasta donde yo sé, Windows carga todo el registro al iniciar sesión y también hace una copia para roaming o para poder cargar la última configuración buena conocida después de una falla importante. Por lo tanto, el uso del registro afecta al sistema incluso si la aplicación nunca se usa.
Phil1970
Además, el registro tiene un límite de tamaño y, si la aplicación aún lo usa, probablemente ese límite se excedería en la mayoría de las computadoras. El registro fue diseñado en un momento en que el sistema tiene menos memoria en MB que la computadora tiene hoy en GB. El registro no fue diseñado para ser tan grande y, por lo tanto, aunque los límites han aumentado, no está optimizado para nuestras necesidades actuales.
Phil1970
3

En mi experiencia, almacenar todas las configuraciones en una tabla de base de datos es la mejor solución. Ni siquiera se preocupe por el rendimiento. Las bases de datos actuales son rápidas y pueden almacenar fácilmente miles de columnas en una tabla. Aprendí esto de la manera difícil, antes de serilizar / deserializar, una pesadilla. Almacenarlo en un archivo o registro local tiene un gran problema: si tiene que admitir su aplicación y la computadora está apagada, el usuario no está frente a ella, no hay nada que pueda hacer ... si las configuraciones están en la base de datos, puede los cambió y viola sin mencionar que puede comparar la configuración ....

Adán
fuente
Y almacenarlos de forma remota también tiene un gran problema cuando la conexión no está disponible ... Muchas aplicaciones escritas para trabajos en línea tienen una experiencia menos que ideal cuando se trabaja sin conexión o, en algún momento, incluso tienen errores que hacen que algunas funciones no funcionen sin conexión incluso si debería no tendrá ningún impacto más que "espiar" cómo se utiliza el dispositivo.
Phil1970
1

Normalmente hago este tipo de cosas definiendo una Serializableclase de configuración personalizada [ ] y simplemente serializándola en el disco. En su caso, podría almacenarlo fácilmente como un blob de cadena en su base de datos SQLite.

Doobi
fuente
0
  1. En todos los lugares en los que he trabajado, la base de datos ha sido obligatoria debido al soporte de la aplicación. Como dijo Adam, es posible que el usuario no esté en su escritorio o que la máquina esté apagada, o es posible que desee cambiar rápidamente la configuración de alguien o asignar a un nuevo usuario una configuración predeterminada (o miembro del equipo).

  2. Si es probable que la configuración aumente a medida que se lanzan nuevas versiones de la aplicación, es posible que desee almacenar los datos como blobs que luego la aplicación puede deserializar. Esto es especialmente útil si usa algo como Prism que descubre módulos, ya que no puede saber qué configuraciones devolverá un módulo. Los blobs pueden estar codificados por nombre de usuario / clave compuesta de máquina. De esa manera, puede tener diferentes configuraciones para cada máquina.

  3. No he usado mucho la clase de Configuración incorporada, así que me abstendré de comentar. :)

Will_Piuvans
fuente
0

Quería usar un archivo de control xml basado en una clase para mi aplicación WPF de escritorio VB.net. El código anterior para hacer todo esto en uno es excelente y me puso en la dirección correcta. En caso de que alguien esté buscando una solución de VB.net, aquí está la clase que construí:

Imports System.IO
Imports System.Xml.Serialization

Public Class XControl

Private _person_ID As Integer
Private _person_UID As Guid

'load from file
Public Function XCRead(filename As String) As XControl
    Using sr As StreamReader = New StreamReader(filename)
        Dim xmls As New XmlSerializer(GetType(XControl))
        Return CType(xmls.Deserialize(sr), XControl)
    End Using
End Function

'save to file
Public Sub XCSave(filename As String)
    Using sw As StreamWriter = New StreamWriter(filename)
        Dim xmls As New XmlSerializer(GetType(XControl))
        xmls.Serialize(sw, Me)
    End Using
End Sub

'all the get/set is below here

Public Property Person_ID() As Integer
    Get
        Return _person_ID
    End Get
    Set(value As Integer)
        _person_ID = value
    End Set
End Property

Public Property Person_UID As Guid
    Get
        Return _person_UID
    End Get
    Set(value As Guid)
        _person_UID = value
    End Set
End Property

End Class
Scott
fuente