Click here to Skip to main content
15,885,985 members
Articles / Programming Languages / C#
Tip/Trick

Easy Loading and Saving of Settings, Stored as a Simple Class

Rate me:
Please Sign up or sign in to vote.
4.00/5 (4 votes)
23 Feb 2022CPOL2 min read 5.5K   7   7
Using a class for storing and saving settings
This tip shows a simple way of using a class as a parameter, and processing the class' properties.

Introduction

While working on an application, I got tired of adding lines for every setting I created, so I thought that there had to be better way of processing them all. This is the result.

This piece of code offers to use a class for settings. To add a new setting, one simply has to create a new property in the class. Settings are stored in a simple class, a setting is a property in that class. No extra code is needed to ensure that it gets saved or loaded. The saver will read all properties and save them all to the file. The loader will create the class, and thereby set default settings. Any value present in the file will be applied to the class. At the end, the class is returned for the software to use.

The Code is Simple

It starts with setting up the storage space and the file name for settings. One may change this as one finds necessary.

Next, for saving, all properties are read and saved to file, one by one.

For reading, the file is read, and if the name exists as a property in the settings class, then it is applied to the class. This also enables to have some validation within the set { } in the settings class.

The class used can be named anything, and it is possible to have multiple files and settings.
This example is using a default file name, settings.config, but one can use any file name to create more.

Example

Here is a simple example of a settings class:

C#
public class Settings
{
    public int Setting1 { get; set; }
    public bool Setting2 { get; set; }
    public double Setting3 { get; set; } = 7.6;
    public DateTime Setting4 { get; set; }
    public string Setting5 { get; set; } = "test";
    public List<string> Setting6 { get; set; } = new List<string>()
                                       { "test1", "test2" };
}

And how it can be used:

C#
var settings = (Settings)GetSettings("Settings"); // name of class, from above
settings.Setting1 = Settings.setting1 + 1;        // change some settings
settings.Setting4 = DateTime.Now;
SaveSettings(settings);                   // save all properties of the class to file		

Types of Settings

As mentioned before, this will read the properties of the class and apply them. As of now, this has implemented the following types: string, boolean, int, double, Datetime and list of strings (List<string>).
For other types, the code will give an exception, but the code is easily expandable.

Storage Location

The present code will use the local application folder, where it will create a folder with name of the executing (your) application.

Error Handling

This example has basic error handling. One can add additional error handling if needed.

Saving a Class

C#
public void SaveSettings(object settings, string filename = "settings.config")
{
    // create storage folder and file name
    string storageFolder = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        Path.GetFileNameWithoutExtension(Application.ExecutablePath));
    if (!Directory.Exists(storageFolder))
        Directory.CreateDirectory(storageFolder);
    if (!Directory.Exists(storageFolder))
        throw new Exception($"Could not create folder {storageFolder}");
    string settingsFileName = Path.Combine(storageFolder, filename);
    
    // create file and process class, save properties one by one
    XmlWriterSettings xmlWriterSettings = new XmlWriterSettings()
    {
        Indent = true,
    };
    XmlWriter writer = XmlWriter.Create(settingsFileName, xmlWriterSettings);
    using (writer)
    {
        writer.WriteStartElement("settings");
        var classType = settings.GetType();
        var props = classType.GetProperties();
        for (int i = 0; i < props.Length; i++)
        {
            string fileSetting = char.ToLower(props[i].Name[0]) + props[i].Name.Substring(1);
            
            if (props[i].PropertyType == typeof(string))
            {
                var sett = classType.GetProperty(props[i].Name).GetValue(settings);
                if (sett != null)
                {
                    string s = sett.ToString();
                    if (!string.IsNullOrEmpty(s))
                        writer.WriteElementString(fileSetting, s);
                }
            }
            else if (props[i].PropertyType == typeof(int))
            {
                writer.WriteElementString(fileSetting, 
                classType.GetProperty(props[i].Name).GetValue(settings).ToString());
            }
            else if (props[i].PropertyType == typeof(double))
            {
                writer.WriteElementString(fileSetting, 
                classType.GetProperty(props[i].Name).GetValue(settings).ToString());
            }
            else if (props[i].PropertyType == typeof(DateTime))
            {
                var dt = (DateTime)(classType.GetProperty(props[i].Name).GetValue(settings));
                writer.WriteElementString(fileSetting, dt.ToOADate().ToString());
            }
            else if (props[i].PropertyType == typeof(bool))
            {
                writer.WriteElementString(fileSetting,
                classType.GetProperty(props[i].Name).GetValue(settings).ToString());
            }
            else if (props[i].PropertyType == typeof(List<string>))
            {
                List<string> values =
                classType.GetProperty(props[i].Name).GetValue(settings) as List<string>;
                string val = string.Join(",", values.ToArray());
                writer.WriteElementString(fileSetting, val);
            }
            else
                throw new Exception($"Unknown setting type found: {props[0].PropertyType}");
        }
        
        writer.WriteEndElement();
        writer.Flush();
    }
}

Loading a Class

Here is the code to read a file and apply properties into the class and return it.
Note that some parts throw an exception, but can be changed to simply returning default settings.
Also note, that the class returns an object, as it is not possible to return the actual class. Therefore, it is needed to typecast the result the class used.

Example:

C#
var mySettings = (MySettingsClass)GetSettings("MySettingsClass"); // here, the class
                                               // is used is named MySettingsClass
C#
public object GetSettings(string settingsClassName, string filename = "settings.config")
{
// get class type and create default class
    var settingsType = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
                        from t in assembly.GetTypes()
                        where t.Name == settingsClassName
                        select t).FirstOrDefault();
    object settings = Activator.CreateInstance(settingsType);
    
// create storage folder and file name
    string storageFolder = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        Path.GetFileNameWithoutExtension(Application.ExecutablePath));
    if (!Directory.Exists(storageFolder))
        Directory.CreateDirectory(storageFolder);
    if (!Directory.Exists(storageFolder))
        throw new Exception($"Could not create folder {storageFolder}");
    string settingsFileName = Path.Combine(storageFolder, filename);
    
// recreate file if missing and return default settings
    if (!File.Exists(settingsFileName))
    {
        SaveSettings(settings);
        return settings;
    }
    
// read and process file
    XmlDocument settingsFile = new XmlDocument();
    try
    {
        settingsFile.Load(settingsFileName);
    }
    catch (Exception ex)
    {
        throw ex;
        // option to return default data
        SaveSettings(settings);
        return settings;
    }
    XmlNode settingsNode = null;
    int n = 0;
    while (n < settingsFile.ChildNodes.Count && settingsNode == null)
    {
        if (settingsFile.ChildNodes[n].Name.ToLower() == "settings")
            settingsNode = settingsFile.ChildNodes[n];
        n++;
    }
    if (settingsNode == null)
    {
        throw new Exception($"Settings section is not found 
                            in settings file {settingsFileName}");
        // option to return default data
        return settings;
    }
    var classType = settings.GetType();
    var props = classType.GetProperties();
    foreach (XmlNode setting in settingsNode.ChildNodes)
    {
        if (setting.ParentNode.Name.ToLower() != "settings")
            break;
        if (setting.NodeType != XmlNodeType.Element)
            continue;
            
        var settingName = props.Where
        (w => string.Compare(w.Name, setting.Name, true) == 0).ToList();
        if (settingName.Count == 0)
            continue;
        if (string.IsNullOrEmpty(settingName[0].Name))
            continue;
            
        // parse setting as type defines
        if (settingName[0].PropertyType == typeof(string))
            classType.GetProperty(settingName[0].Name).SetValue(settings, setting.InnerText);
        else if (settingName[0].PropertyType == typeof(int))
        {
            int val = 0;
            if (int.TryParse(setting.InnerText, out val))
                classType.GetProperty(settingName[0].Name).SetValue(settings, val);
        }
        else if (settingName[0].PropertyType == typeof(double))
        {
            double val = 0;
            if (double.TryParse(setting.InnerText, out val))
                classType.GetProperty(settingName[0].Name).SetValue(settings, val);
        }
        else if (settingName[0].PropertyType == typeof(DateTime))
        {
            double val = 0;
            if (double.TryParse(setting.InnerText, out val))                        
                classType.GetProperty(settingName[0].Name).SetValue
                (settings, DateTime.FromOADate(val));
        }
        else if (settingName[0].PropertyType == typeof(bool))
        {
            bool val = (string.Compare("true", 
            setting.InnerText, true) == 0) || setting.InnerText == "1";
            classType.GetProperty(settingName[0].Name).SetValue(settings, val);
        }
        else if (settingName[0].PropertyType == typeof(List<string>))
        {
            string val = setting.InnerText.Trim();
            if (string.IsNullOrEmpty(val))
            {
                classType.GetProperty(settingName[0].Name).SetValue
                (settings, new List<string>());
                continue;
            }
            List<string> values = val.Split(',').ToList();
            classType.GetProperty(settingName[0].Name).SetValue(settings, values);
        }
        else
            throw new Exception
            ($"Unknown setting type found: {settingName[0].PropertyType}");
    }
    return settings;
}

Conclusion

This is just a simple way of using a class as a parameter, and processing the class' properties. It has the most common types, but can easily be expanded. Also, the file name and location is easily changed.
Enjoy!

History

  • 23rd February, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Andy Missico12-Mar-22 7:51
Andy Missico12-Mar-22 7:51 
GeneralMy vote of 5 Pin
Michael Sydney Balloni24-Feb-22 10:53
professionalMichael Sydney Balloni24-Feb-22 10:53 
Suggestionshould be using standard xml or json serialization Pin
Adam O'Neil24-Feb-22 8:14
Adam O'Neil24-Feb-22 8:14 
QuestionWhy not simply use XmlSerializer to Load and save properties? Pin
Andreas Weiden24-Feb-22 5:06
Andreas Weiden24-Feb-22 5:06 
AnswerRe: Why not simply use XmlSerializer to Load and save properties? Pin
David Burt24-Feb-22 12:00
professionalDavid Burt24-Feb-22 12:00 
GeneralRe: Why not simply use XmlSerializer to Load and save properties? Pin
Sonnich Jensen3-Mar-22 6:06
Sonnich Jensen3-Mar-22 6:06 
QuestionCopy the Properties of one control to another without using Reflection Pin
Member 1489067823-Feb-22 19:40
Member 1489067823-Feb-22 19:40 
If it's so simple, why is reflection used to copy the properties of one control to another? write me to:zequion @ yahoo . es

Copy Properties from Control

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.