Settings class

Stores data such as program settings in an XML file and takes care about easy access, safe storage, change notification and control binding.

Archived content: This source code is currently inactive and may be outdated or no longer maintained or functional.

Diese Klasse wurde durch SettingsAdapterFactory ersetzt. SettingsAdapterFactory generiert eine dynamische Implementierung einer Schnittstelle mit Eigenschaften, die an einen SettingsStore gebunden ist und INotifyPropertyChanged implementiert. Das XML-Dateiformat ist kompatibel mit dieser Settings-Klasse und vorhandene Dateien können per Code aktualisiert werden.

In vielen Anwendungen besteht der Bedarf, Konfigurationseinstellungen abzuspeichern. Die Geschichte hat bewiesen, dass es dafür unzählige verschiedene Möglichkeiten gibt. Eine davon ist diese allgemein gehaltene und dadurch überall gut wiederverwendbare Settings-Klasse. Sie unterstützt das Speichern einzelner Werte unter einem Schlüssel. Im Grunde ist die Datenstruktur eine flache Liste. Jedem Schlüssel, beliebigen Zeichenketten, kann ein Wert zugeordnet werden. Der Datentyp des Werts bleibt beim Speichern erhalten und kann beim Lesen in einem gewissen Rahmen automatisch konvertiert werden. Die folgenden Datentypen werden unterstützt: string (String), int (Int32), long (Int64), bool (Boolean) und jeweils Arrays dieser Typen. Weitere Typen können der Klasse jedoch hinzugefügt werden. Zur Schlüsselvergabe empfiehlt sich das Konzept, das auch in Mozilla Firefox verwendet wird: Hierarchische Zeichenketten, deren Abschnitte jeweils durch einen Punkt getrennt werden (siehe auch im Beispiel unten).

Die Speicherung der Daten erfolgt in der angegebenen XML-Datei. Dieses Dateiformat ermöglicht auch eine manuelle Bearbeitung der Datei oder den Zugriff mit anderen Programmen. Beim Instanziieren der Settings-Klasse werden alle Daten aus der Datei gelesen. Diese Daten können dann mit den Get-Methoden abgerufen werden. Beim Hinzufügen oder Ändern eines Eintrags wird dieser mit einer kurzen Verzögerung gleich in der Datei gespeichert. So bleiben auch bei einem Programmabsturz bereits getätigte Einstellungen gespeichert (Frustquelle in vielen Programmen!). Die Verzögerung von etwa einer Sekunde ermöglicht das Sammeln mehrerer Änderungen zum Schreiben und verbessert die Performance deutlich. Durch das Disposable-Pattern wird sichergestellt, dass auch beim Beenden des Programms noch ausstehende Änderungen geschrieben werden. Man braucht sich also keine Gedanken darüber zu machen, wann und wie die Einstellungen gespeichert werden. Es reicht völlig aus, die Klasse zu instanziieren und die Get- und Set-Methoden zu verwenden.

Eine weitere Besonderheit dieser Klasse ist die Änderungsbenachrichtigung. Wenn ein Schlüssel, für den eine Benachrichtigungsfunktion eingetragen ist, geändert wird, wird automatisch die Funktion aufgerufen, die dann auf den neuen Wert reagieren kann. Für diesen Zweck können auch Windows-Forms-Controls mit einem Schlüssel verknüpft werden, so dass das Betätigen einer CheckBox ausreicht, um am anderen Ende der Anwendung eine Darstellungsoption zu ändern. Dadurch können Konfigurationsfenster gestaltet werden, deren Eingaben sofort angewendet werden, und in denen keine OK-Schaltfläche mehr notwendig ist.

Compatibility: .NET Version 2.0 or newer

Beispiel

Der folgende Beispielcode zeigt, wie die Settings-Klasse in einem Fenster verwendet werden kann:

class Form1 : Form
{
    private Settings settings;
    private string settingsFileName = "demo.conf";
   
    public Form1()
    {
        // Load settings
        settings = new Settings(settingsFileName);
       
        // Set some keys to default values if they're yet unset
        settings.SetDefault("appearance.list.columns.qth.visible", true, false);
        // Also register a notification function for value changes
        settings.AddHandler("appearance.list.columns.qth.visible",
            ColumnQTHVisibleChanged, true);
        settings.SetDefault("appearance.list.columns.freq.precision", 4, false);
        settings.AddHandler("appearance.list.columns.freq.precision",
            ColumnFreqPrecisionChanged, true);

        // Bind Windows Forms controls to settings
        settings.BindControl("appearance.list.columns.qth.visible",
            ShowColumnQTHCheck, true);   // a CheckBox
        settings.BindControl("appearance.list.columns.freq.precision",
            FreqPrecisionNum, true);   // a NumericUpDown

        // Restore the window position and state
        Point location = new Point(
            settings.GetInt("window.left", Left),
            settings.GetInt("window.top", Top));
        Size size = new Size(
            settings.GetInt("window.width", Width),
            settings.GetInt("window.height", Height));
        FormWindowState state =
            (FormWindowState) settings.GetInt("window.state", (int) WindowState);
        // Prevent the application from starting minimised if closed minimised last time
        if (state == FormWindowState.Minimized) state = FormWindowState.Normal;
        Location = location;
        Size = size;
        WindowState = state;

        // Read a list of values
        List<string> fields =
            new List<string>(settings.GetStringArray("startup.filter.fields"));
        db.FilterCall = toolMenuCall.Checked = fields.Contains("call");
        db.FilterDate = toolMenuDate.Checked = fields.Contains("date");
        db.FilterFreq = toolMenuFreq.Checked = fields.Contains("freq");
        db.FilterMode = toolMenuMode.Checked = fields.Contains("mode");
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        // Ensure all settings are written to disk
        settings.Dispose();
    }

    private void Form1_LocationOrSizeChanged(object sender, EventArgs e)
    {
        // Save current windows location and size
        if (WindowState == FormWindowState.Normal)
        {
            settings.Set("window.left", Left);
            settings.Set("window.top", Top);
            settings.Set("window.width", Width);
            settings.Set("window.height", Height);
        }
        settings.Set("window.state", (int) WindowState);
    }

    // Called whenever the "appearance.list.columns.qth.visible" setting has changed.
    // Reflects the new value in the UI.
    private void ColumnQTHVisibleChanged(string key, object oldValue, object newValue)
    {
        if ((bool) newValue == false)
        {
            QsoList.Columns.Remove(columnHeaderQTH);
        }
        else
        {
            if (!QsoList.Columns.Contains(columnHeaderQTH))
            {
                QsoList.Columns.Insert(10, columnHeaderQTH);
            }
        }
    }

    // Called whenever the "appearance.list.columns.freq.precision" setting has changed.
    // Reflects the new value in the UI.
    private void ColumnFreqPrecisionChanged(string key, object oldValue, object newValue)
    {
        int i = (int) newValue;
        QSO.precision = (ushort) i;
    }
}

Die XML-Datei sieht dann in etwa so aus:

<?xml version="1.0" encoding="utf-8"?>
<settings>
    <entry key="appearance.list.columns.freq.precision" type="int">4</entry>
    <entry key="appearance.list.columns.qth.visible" type="bool">true</entry>
    <entry key="startup.filter.fields" type="string-array">
        <item>call</item>
        <item>date</item>
    </entry>
    <entry key="window.height" type="int">592</entry>
    <entry key="window.left" type="int">137</entry>
    <entry key="window.state" type="int">0</entry>
    <entry key="window.top" type="int">117</entry>
    <entry key="window.width" type="int">873</entry>
</settings>

Dieser Beispielcode stammt aus meiner Anwendung Amateur Radio Log, für die diese Klasse ursprünglich entwickelt wurde.

Download

Settings.cs18.3 KiBQuelltext der Settings-Klasse

Licence and terms of use

Copying and distribution of this file, with or without modification, are permitted provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. (GNU All-Permissive licence)

Statistic data

  • Created on 2006-08-01, updated on 2012-04-11.
  • First used in ARLog: Amateur Radio Logbook.
  • Ca. 290 lines of code, estimated development costs: 290 - 1 200 €