c # - Ansatz zum Speichern von Benutzereinstellungen in einer WPF-Anwendung?

84

Welchen Ansatz empfehlen Sie, um Benutzereinstellungen in einer WPF-Windows-Anwendung (Desktop) beizubehalten? Beachten Sie, dass der Benutzer seine Einstellungen zur Laufzeit ändern und dann die Anwendung schließen kann. Wenn die Anwendung später gestartet wird, verwendet die Anwendung die aktuellen Einstellungen. Tatsächlich sieht es dann so aus, als würden sich die Anwendungseinstellungen nicht ändern.

Q1 - Datenbank oder anderer Ansatz? Ich habe eine SQLite-Datenbank, die ich sowieso verwenden werde, daher wäre die Verwendung einer Tabelle in der Datenbank so gut wie jeder Ansatz?

F2 - Wenn Datenbank: Welches Datenbanktabellendesign? Eine Tabelle mit Spalten für verschiedene Datentypen , die man haben könnte (zB string, long, DateTimeusw.) oder nur eine Tabelle mit einem String für den Wert , auf das Sie serialisiert haben und de-serialize der Werte? Ich denke, das erste wäre einfacher, und wenn es nicht viele Einstellungen gibt, ist der Overhead nicht viel?

F3 - Könnten hierfür Anwendungseinstellungen verwendet werden? Wenn ja, sind spezielle Aufgaben erforderlich, um die Persistenz hier zu ermöglichen? Was würde in diesem Fall auch in Bezug auf die Verwendung des "Standard" -Werts im Anwendungseinstellungen-Designer passieren? Würde die Standardeinstellung alle Einstellungen überschreiben, die zwischen dem Ausführen der Anwendung gespeichert wurden? (oder müssten Sie NICHT den Standardwert verwenden)

Greg
quelle
@ Alle neuen Benutzer Es wird bevorzugt, wenn Sie separate Fragen stellen können, anstatt Ihre Fragen zu einer zu kombinieren. Auf diese Weise können die Personen, die Ihre Frage beantworten, und auch andere nach mindestens einer Ihrer Fragen suchen. Vielen Dank!
Hille

Antworten:

80

Sie können hierfür die Anwendungseinstellungen verwenden . Die Verwendung der Datenbank ist angesichts der Zeit, die zum Lesen und Schreiben der Einstellungen benötigt wird, nicht die beste Option (insbesondere, wenn Sie Webdienste verwenden).

Hier sind einige Links, die erklären, wie dies erreicht und in WPF verwendet werden kann -

Benutzereinstellungen in WPF

Schneller WPF-Tipp: Wie binde ich mich an WPF-Anwendungsressourcen und -einstellungen?

Ein konfigurierbares Fenster für WPF

Akjoshi
quelle
22

Aktualisieren : Heutzutage würde ich JSON verwenden.

Ich bevorzuge es auch, mit der Serialisierung in die Datei zu gehen. XML-Dateien erfüllen fast alle Anforderungen. Sie können das ApplicationSettingsBuild-In verwenden, aber diese haben einige Einschränkungen und ein definiertes, aber (für mich) sehr seltsames Verhalten, in dem sie gespeichert sind. Ich habe sie oft benutzt und sie funktionieren. Aber wenn Sie die volle Kontrolle darüber haben möchten, wie und wo sie gespeichert wurden, verwende ich einen anderen Ansatz.

  1. Bilden Sie eine Klasse irgendwo mit all Ihren Einstellungen. Ich habe es genanntMySettings
  2. Implementieren Sie Save and Read für die Persistenz
  3. Verwenden Sie sie in Ihrem Anwendungscode

Vorteile:

  • Sehr einfacher Ansatz.
  • Eine Klasse für Einstellungen. Belastung. Sparen.
  • Alle Ihre Einstellungen sind typsicher.
  • Sie können die Logik vereinfachen oder an Ihre Bedürfnisse anpassen (Versionierung, viele Profile pro Benutzer usw.)
  • Es funktioniert auf jeden Fall sehr gut (Datenbank, WinForms, WPF, Service usw.)
  • Sie können festlegen, wo die XML-Dateien gespeichert werden sollen.
  • Sie können sie finden und entweder per Code oder manuell bearbeiten
  • Es funktioniert für jede Bereitstellungsmethode, die ich mir vorstellen kann.

Nachteile: - Sie müssen überlegen, wo Sie Ihre Einstellungsdateien speichern sollen. (Sie können aber einfach Ihren Installationsordner verwenden)

Hier ist ein einfaches Beispiel (nicht getestet) -

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;
        }
    }
}

Und hier ist, wie man es benutzt. Es ist möglich, Standardwerte zu laden oder sie mit den Benutzereinstellungen zu überschreiben, indem Sie nur überprüfen, ob Benutzereinstellungen vorhanden sind:

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);
    }
}

Vielleicht lässt sich jemand von diesem Ansatz inspirieren. So mache ich das jetzt seit vielen Jahren und ich bin ziemlich zufrieden damit.

Matte
quelle
1
Zum Nachteil wäre es, dass Sie den Einstellungsdesigner nicht mehr haben, so dass es etwas weniger benutzerfreundlich ist, wenn beide funktionieren würden.
Phil1970
3
Ich stimme voll und ganz dem "sehr seltsamen Verhalten, in dem sie gespeichert sind" zu. Genau aus diesem Grund verwende ich Ihren Ansatz. +1.
Hannish
Wenn Sie eine neue Frage haben, fragen Sie es bitte durch Klicken Frage stellen Taste.
Mat
12

Sie können Ihre Einstellungsinformationen ab StringsXML in der speichern Settings.Default. Erstellen Sie einige Klassen, um Ihre Konfigurationsdaten zu speichern und sicherzustellen, dass dies der Fall ist [Serializable]. Mit den folgenden Hilfsprogrammen können Sie dann Instanzen dieser Objekte - oder List<T>(oder Arrays T[]usw.) davon - serialisieren String. Speichern Sie jede dieser verschiedenen Zeichenfolgen in einem eigenen Settings.DefaultSteckplatz in Ihrer WPF-AnwendungSettings .

Um die Objekte beim nächsten Start der App wiederherzustellen, lesen Sie die gewünschte SettingsZeichenfolge und Deserializeden erwarteten Typ T(für den diesmal explizit als Typargument angegeben werden muss 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
quelle
6

Der langfristig typischste Ansatz für diese Frage lautet: Isolierter Speicher.

Serialisieren Sie Ihren Steuerungsstatus in XML oder ein anderes Format (besonders einfach, wenn Sie Abhängigkeitseigenschaften mit WPF speichern) und speichern Sie die Datei dann im isolierten Speicher des Benutzers.

Wenn Sie die App-Einstellungsroute einschlagen möchten, habe ich an einer Stelle selbst etwas Ähnliches versucht ... obwohl der folgende Ansatz leicht für die Verwendung von isoliertem Speicher angepasst werden könnte:

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();
    }
}

.....und....

  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
quelle
5

Neben einer Datenbank können Sie auch folgende Optionen zum Speichern benutzerbezogener Einstellungen verwenden

  1. Registrierung unter HKEY_CURRENT_USER

  2. in einer Datei im AppDataOrdner

  3. Verwenden der SettingsDatei in WPF und Festlegen des Bereichs als Benutzer

ajay_whiz
quelle
2
Vorschlag 1 ist der Grund, warum Anwendungen Windows verlangsamen und Registrierungsschlüssel besser nicht mit etwas füllen, das in einer IMO-Datei besser gemacht wird.
Konsole
1
@Console, Schreiben einer Datei auf eine Festplatte verlangsamt (verschleißt) SSD, Schreiben von Daten in eine Datenbank verlangsamt die Datenbank. Was ist dann Ihre Option? Die Windows-Registrierung soll als einer der Orte zum Speichern von Einstellungen verwendet werden .
Sinatr
1
Sie haben Recht, ich denke, es ist wichtig zu erwähnen, dass die Registrierung einige Nachteile hat, wenn jede Anwendung dort hundert Benutzereinstellungen speichert.
Konsole
@Sinatr Ursprünglich war die Registrierung für diesen Zweck vorgesehen ... aber sie war nicht für die Verarbeitung großer Datenmengen konzipiert. Daher hat Microsoft zu einem bestimmten Zeitpunkt im Verlauf empfohlen, die Verwendung einzustellen. Soweit ich weiß, lädt Windows die gesamte Registrierung bei der Anmeldung und erstellt auch eine Kopie für das Roaming oder um die letzte bekannte gute Konfiguration nach einem schweren Absturz laden zu können. Die Verwendung der Registrierung wirkt sich daher auch dann auf das System aus, wenn die Anwendung nie verwendet wird.
Phil1970
Außerdem hat die Registrierung eine Größenbeschränkung, und wenn sie noch von der Anwendung verwendet wurde, würde die Beschränkung wahrscheinlich auf den meisten Computern überschritten. Die Registrierung wurde zu einer Zeit entwickelt, in der das System weniger Speicher in MB hat als der Computer heute in GB. Die Registrierung wurde nicht so groß gestaltet, und obwohl die Grenzwerte gestiegen sind, ist sie nicht für unsere aktuellen Anforderungen optimiert.
Phil1970
3

Nach meiner Erfahrung ist das Speichern aller Einstellungen in einer Datenbanktabelle die beste Lösung. Mach dir keine Sorgen um die Leistung. Die heutigen Datenbanken sind schnell und können problemlos Tausende von Spalten in einer Tabelle speichern. Ich habe das auf die harte Tour gelernt - bevor ich den Albtraum serilisierte / deserialisierte -. Das Speichern in einer lokalen Datei oder Registrierung hat ein großes Problem: Wenn Sie Ihre App unterstützen müssen und der Computer ausgeschaltet ist, ist der Benutzer nicht davor. Sie können nichts tun. Wenn sich die Einstellungen in der Datenbank befinden, können Sie dies geändert sie und Bratsche nicht zu erwähnen, dass Sie die Einstellungen vergleichen können ....

Adam
quelle
Das Remote-Speichern hat auch ein großes Problem, wenn die Verbindung nicht verfügbar ist ... Viele Anwendungen, die für Online-Arbeiten geschrieben wurden, haben eine weniger ideale Erfahrung beim Offline-Arbeiten oder manchmal sogar Fehler, die dazu führen, dass einige Funktionen nicht offline funktionieren, selbst wenn dies der Fall sein sollte Sie haben keine anderen Auswirkungen als das "Ausspionieren" der Verwendung des Geräts.
Phil1970
1

Normalerweise mache ich so etwas, indem ich eine benutzerdefinierte [ Serializable] Einstellungsklasse definiere und sie einfach auf die Festplatte serialisiere. In Ihrem Fall können Sie es genauso einfach wie einen String-Blob in Ihrer SQLite-Datenbank speichern.

Doobi
quelle
0
  1. An allen Orten, an denen ich gearbeitet habe, war die Datenbank aufgrund der Anwendungsunterstützung obligatorisch. Wie Adam sagte, ist der Benutzer möglicherweise nicht an seinem Schreibtisch oder der Computer ist ausgeschaltet, oder Sie möchten schnell die Konfiguration einer anderen Person ändern oder einem neuen Joiner eine Standardkonfiguration (oder die Konfiguration eines Teammitglieds) zuweisen.

  2. Wenn die Einstellungen wahrscheinlich zunehmen, wenn neue Versionen der Anwendung veröffentlicht werden, möchten Sie die Daten möglicherweise als Blobs speichern, die dann von der Anwendung deserialisiert werden können. Dies ist besonders nützlich, wenn Sie Prism verwenden, das Module erkennt, da Sie nicht wissen können, welche Einstellungen ein Modul zurückgeben wird. Die Blobs können über den zusammengesetzten Benutzernamen / Maschinenschlüssel eingegeben werden. Auf diese Weise können Sie für jede Maschine unterschiedliche Einstellungen vornehmen.

  3. Ich habe die eingebaute Klasse "Einstellungen" nicht viel verwendet, daher verzichte ich auf Kommentare. :) :)

Will_Piuvans
quelle
0

Ich wollte eine XML-Steuerdatei basierend auf einer Klasse für meine VB.net-Desktop-WPF-Anwendung verwenden. Der obige Code, um dies alles in einem zu tun, ist ausgezeichnet und bringt mich in die richtige Richtung. Für den Fall, dass jemand nach einer VB.net-Lösung sucht, ist hier die Klasse, die ich erstellt habe:

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
quelle