Saturday, May 1, 2010

A simple class to read and write application settings

I'll come straight to the point. All I want to do is save a few setting for my little .NET application like

MaxSizeOfText=256.

Ok, that not all. I want each user to have his own settings. I can do this by storing such settings to a file in the user's appData folder.

Programming in the .NET world is usually pretty easy though a bit frustrating because at times it is painfully easy. There are also times when exisitng classes can get the work done but finding out how exaclty to use these classes is so darned unintuitive that its easier to write the functionality all by yourself.

I consider all classes present in the base-class-library revolving around reading / writing application configuration a big fiasco. There aren't many areas which are as confusing as this. With each version of .NET new ways to access appSettings keep cropping up. Come on, how difficult is to define an easy to understand programmatic access to some XML data? and if you can't do that why not just hide those classes from end developers, we are capable of writing our own.

Here is a class I quickly wrote which allows you to store application settings in a 'name'-'value' form in XML. The file can be located anywhere on your computer. It has just 3 menthods for interacting with it so it's very simple to use. Settings can be added without any special methods, setting can be read and setting can be removed. Minimum requirement, a target file should be present with atleast following xml:


Rest is taken care of by the class. I am sure there must be lots of improvement that can be made and I would appreciate any comments.

The class diagrams is presented below:

class diagram

Here is the complete code for the XmlSettings class:


// ++
// (C) Copyright Siddharth Barman, 2010.
// Copyright notice: You are free to use this material with or without the copyright notice.
// Author is not reponsible for any damage/problems arising out of the use of this material.
// --

using System.Xml;

namespace Sid.Utilities
{
    /// <summary>
    /// Allows reading and writing settings to XML files. The XML files should have
    /// the following structure <settings><setting name="ABC" value="XYZ" /></settings>
    /// </summary>
    public class XmlSettings
    {
        #region Public interface

        /// <summary>
        /// Path to the settings file. The file must exist.
        /// </summary>
        /// <param name="file"></param>
        public XmlSettings(string file)
        {
            this.file = file;
            doc = new XmlDocument();
            doc.Load(file);
        }

        /// <summary>
        /// Returns the settings file being used.
        /// </summary>
        public string SettingsFile
        {
            get { return file; }
        }

        /// <summary>
        /// Allows a setting to be read or written. Each setting is referred by it's name. 
        /// Null is returned if a setting does not exist while reading. 
        /// A new setting is created if it does not exist while writing.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string this[string name]
        {
            get 
            {
                return ReadSetting(name);
            }
            set 
            {
                SetSetting(name, value);
            }
        }

        public void Remove(string name)
        {
            XmlNode node = GetSettingNode(name);

            if (node != null)
            {
                RootNode.RemoveChild(node);
            }
        }

        public void Save()
        {
            doc.Save(file);
        }

        #endregion

        #region Protected and Private members

        protected XmlNode RootNode
        {
            get
            {
                return doc.SelectSingleNode("/settings");
            }
        }

        protected string ReadSetting(string name)
        {
            XmlNode node = GetSettingNode(name);

            if (node == null || node.Attributes.Count == 0 || node.Attributes["value"] == null)
            {
                return null;
            }

            return node.Attributes["value"].Value;
        }

        protected void SetSetting(string name, string value)
        {
            XmlNode node = GetSettingNode(name);

            if (node == null)
            {
                node = CreateSettingNode(name);
            }

            node.Attributes["value"].Value = value;
        }

        protected XmlNode GetSettingNode(string name)
        {
            return doc.SelectSingleNode(string.Format("/settings/setting[@name='{0}']", name));
        }

        protected XmlNode CreateSettingNode(string name)
        {
            XmlNode node = doc.CreateNode(XmlNodeType.Element, "setting", string.Empty);
            RootNode.AppendChild(node);

            XmlAttribute nameAtt = doc.CreateAttribute("name");
            node.Attributes.Append(nameAtt);
            nameAtt.Value = name;

            XmlAttribute valueAtt = doc.CreateAttribute("value");
            node.Attributes.Append(valueAtt);

            return node;
        }

        protected string file;
        protected XmlDocument doc;

        #endregion
    }
}