Easy Loading and Saving of Settings, Stored as a Simple Class
source link: https://www.codeproject.com/Tips/5325914/Easy-Loading-and-Saving-of-Settings-Stored-as-a-Si
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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:
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:
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
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:
var mySettings = (MySettingsClass)GetSettings("MySettingsClass"); // here, the class // is used is named MySettingsClass
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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK