C# Portable Settings Provider
While all of the applications I write in .NET do not require installation, the restrictions that Microsoft places on using their built-in settings provider tend to make the programs less than portable.
By default, configuration files generated by .NET applications reside in a complicated path located under a user’s AppData folder, or Application Data folder for those of you still using Windows XP. For instance, the configuration file used for Suction on my system is:
C:\Users\mike\AppData\Local\MN\Suction.exe_Url_0k4nu2jvc3qbgln4l3jyokwo1uqvgcs5
This particular configuration path reflects a copy of Suction located on my desktop. If I were to move the executable to another directory, a separate configuration file would be created under a new path in my AppData directory. Clearly this is less than ideal, as well as incredibly flexible.
Developers have asked Microsoft to grant the ability to change the location of these configuration files using the .NET API through official feedback channels, but Microsoft has refused. The boys in Redmond responded saying that this behavior is by design and was created in order to prevent one application from overwriting another program’s configuration file. They suggest that users create their own settings provider if they want to choose where configuration files are saved. Their inflexibility is not only annoying, it is stifling.
I have had more than a few people who use my software ask me to localize the configuration files so that the applications are truly portable. I searched high and low for a easy solution to the problem, but I could not find one for the life of me. I found examples and pieces of code here and there, but nothing that would create the kind of configuration files I was looking for. What I was searching for was a portable settings provider that was 100% back-compatible with existing application configuration files. I finally decided that if I wanted that kind of functionality, I would have to build it myself. Continue reading to view and download my C# Portable Settings Provider.
This code is still currently in development, so it might contain bugs, it might not be pretty, and it might not be compatible with every specialize data structure C# .NET has to offer. The good thing is that it is free for you to alter, provided you follow the licensing guidelines. If you happen to add something useful or have a suggestion, please let me know at: mike [at] geek [dash] republic [dot] com.
To add the Portable Settings Provider to your project do the following:
- Download the source file or copy the code below, saving it as PortableSettingsProvider.cs
- Add the file to your C# .NET solution as an Existing Item
- Add a reference to the System.Configuration .NET Framework item in your solution
- Configure your application’s settings to use the PortableSettingsProvider as shown at the bottom of this post
Good luck with your programs and send me any feedback you might have!
/*************************************************************
* PortableSettingsProvider.cs
* Portable Settings Provider for C# applications
*
* 2010- Michael Nathan
* http://www.Geek-Republic.com
*
* Licensed under Creative Commons CC BY-SA
* http://creativecommons.org/licenses/by-sa/3.0/legalcode
*
*
*
*
*************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Xml;
using System.Xml.Serialization;
public class PortableSettingsProvider : SettingsProvider
{
// Define some static strings later used in our XML creation
// XML Root node
const string XMLROOT = "configuration";
// Configuration declaration node
const string CONFIGNODE = "configSections";
// Configuration section group declaration node
const string GROUPNODE = "sectionGroup";
// User section node
const string USERNODE = "userSettings";
// Application Specific Node
string APPNODE = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".Properties.Settings";
private System.Xml.XmlDocument xmlDoc = null;
// Override the Initialize method
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(this.ApplicationName, config);
}
// Override the ApplicationName property, returning the solution name. No need to set anything, we just need to
// retrieve information, though the set method still needs to be defined.
public override string ApplicationName
{
get
{
return (System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}
set
{
return;
}
}
// Simply returns the name of the settings file, which is the solution name plus ".config"
public virtual string GetSettingsFilename()
{
return ApplicationName + ".config";
}
// Gets current executable path in order to determine where to read and write the config file
public virtual string GetAppPath()
{
return new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName;
}
// Retrieve settings from the configuration file
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext sContext, SettingsPropertyCollection settingsColl)
{
// Create a collection of values to return
SettingsPropertyValueCollection retValues = new SettingsPropertyValueCollection();
// Create a temporary SettingsPropertyValue to reuse
SettingsPropertyValue setVal;
// Loop through the list of settings that the application has requested and add them
// to our collection of return values.
foreach (SettingsProperty sProp in settingsColl)
{
setVal = new SettingsPropertyValue(sProp);
setVal.IsDirty = false;
setVal.SerializedValue = GetSetting(sProp);
retValues.Add(setVal);
}
return retValues;
}
// Save any of the applications settings that have changed (flagged as "dirty")
public override void SetPropertyValues(SettingsContext sContext, SettingsPropertyValueCollection settingsColl)
{
// Set the values in XML
foreach (SettingsPropertyValue spVal in settingsColl)
{
SetSetting(spVal);
}
// Write the XML file to disk
try
{
XMLConfig.Save(System.IO.Path.Combine(GetAppPath(), GetSettingsFilename()));
}
catch (Exception ex)
{
// Create an informational message for the user if we cannot save the settings.
// Enable whichever applies to your application type.
// Uncomment the following line to enable a MessageBox for forms-based apps
System.Windows.Forms.MessageBox.Show(ex.Message, "Error writting configuration file to disk", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
// Uncomment the following line to enable a console message for console-based apps
//Console.WriteLine("Error writing configuration file to disk: " + ex.Message);
}
}
private XmlDocument XMLConfig
{
get
{
// Check if we already have accessed the XML config file. If the xmlDoc object is empty, we have not.
if (xmlDoc == null)
{
xmlDoc = new XmlDocument();
// If we have not loaded the config, try reading the file from disk.
try
{
xmlDoc.Load(System.IO.Path.Combine(GetAppPath(), GetSettingsFilename()));
}
// If the file does not exist on disk, catch the exception then create the XML template for the file.
catch (Exception)
{
// XML Declaration
// <?xml version="1.0" encoding="utf-8"?>
XmlDeclaration dec = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
xmlDoc.AppendChild(dec);
// Create root node and append to the document
// <configuration>
XmlElement rootNode = xmlDoc.CreateElement(XMLROOT);
xmlDoc.AppendChild(rootNode);
// Create Configuration Sections node and add as the first node under the root
// <configSections>
XmlElement configNode = xmlDoc.CreateElement(CONFIGNODE);
xmlDoc.DocumentElement.PrependChild(configNode);
// Create the user settings section group declaration and append to the config node above
// <sectionGroup name="userSettings"...>
XmlElement groupNode = xmlDoc.CreateElement(GROUPNODE);
groupNode.SetAttribute("name", USERNODE);
groupNode.SetAttribute("type", "System.Configuration.UserSettingsGroup");
configNode.AppendChild(groupNode);
// Create the Application section declaration and append to the groupNode above
// <section name="AppName.Properties.Settings"...>
XmlElement newSection = xmlDoc.CreateElement("section");
newSection.SetAttribute("name", APPNODE);
newSection.SetAttribute("type", "System.Configuration.ClientSettingsSection");
groupNode.AppendChild(newSection);
// Create the userSettings node and append to the root node
// <userSettings>
XmlElement userNode = xmlDoc.CreateElement(USERNODE);
xmlDoc.DocumentElement.AppendChild(userNode);
// Create the Application settings node and append to the userNode above
// <AppName.Properties.Settings>
XmlElement appNode = xmlDoc.CreateElement(APPNODE);
userNode.AppendChild(appNode);
}
}
return xmlDoc;
}
}
// Retrieve values from the configuration file, or if the setting does not exist in the file,
// retrieve the value from the application's default configuration
private object GetSetting(SettingsProperty setProp)
{
object retVal;
try
{
// Search for the specific settings node we are looking for in the configuration file.
// If it exists, return the InnerText or InnerXML of its first child node, depending on the setting type.
// If the setting is serialized as a string, return the text stored in the config
if (setProp.SerializeAs.ToString() == "String")
{
return XMLConfig.SelectSingleNode("//setting[@name='" + setProp.Name + "']").FirstChild.InnerText;
}
// If the setting is stored as XML, deserialize it and return the proper object. This only supports
// StringCollections at the moment - I will likely add other types as I use them in applications.
else
{
string settingType = setProp.PropertyType.ToString();
string xmlData = XMLConfig.SelectSingleNode("//setting[@name='" + setProp.Name + "']").FirstChild.InnerXml;
XmlSerializer xs = new XmlSerializer(typeof(string[]));
string[] data = (string[])xs.Deserialize(new XmlTextReader(xmlData, XmlNodeType.Element, null));
switch (settingType)
{
case "System.Collections.Specialized.StringCollection":
StringCollection sc = new StringCollection();
sc.AddRange(data);
return sc;
default:
return "";
}
}
}
catch (Exception)
{
// Check to see if a default value is defined by the application.
// If so, return that value, using the same rules for settings stored as Strings and XML as above
if ((setProp.DefaultValue != null))
{
if (setProp.SerializeAs.ToString() == "String")
{
retVal = setProp.DefaultValue.ToString();
}
else
{
string settingType = setProp.PropertyType.ToString();
string xmlData = setProp.DefaultValue.ToString();
XmlSerializer xs = new XmlSerializer(typeof(string[]));
string[] data = (string[])xs.Deserialize(new XmlTextReader(xmlData, XmlNodeType.Element, null));
switch (settingType)
{
case "System.Collections.Specialized.StringCollection":
StringCollection sc = new StringCollection();
sc.AddRange(data);
return sc;
default: return "";
}
}
}
else
{
retVal = "";
}
}
return retVal;
}
private void SetSetting(SettingsPropertyValue setProp)
{
// Define the XML path under which we want to write our settings if they do not already exist
XmlNode SettingNode = null;
try
{
// Search for the specific settings node we want to update.
// If it exists, return its first child node, (the <value>data here</value> node)
SettingNode = XMLConfig.SelectSingleNode("//setting[@name='" + setProp.Name + "']").FirstChild;
}
catch (Exception)
{
SettingNode = null;
}
// If we have a pointer to an actual XML node, update the value stored there
if ((SettingNode != null))
{
if (setProp.Property.SerializeAs.ToString() == "String")
{
SettingNode.InnerText = setProp.SerializedValue.ToString();
}
else
{
// Write the object to the config serialized as Xml - we must remove the Xml declaration when writing
// the value, otherwise .Net's configuration system complains about the additional declaration.
SettingNode.InnerXml = setProp.SerializedValue.ToString().Replace(@"<?xml version=""1.0"" encoding=""utf-16""?>", "");
}
}
else
{
// If the value did not already exist in this settings file, create a new entry for this setting
// Search for the application settings node (<Appname.Properties.Settings>) and store it.
XmlNode tmpNode = XMLConfig.SelectSingleNode("//" + APPNODE);
// Create a new settings node and assign its name as well as how it will be serialized
XmlElement newSetting = xmlDoc.CreateElement("setting");
newSetting.SetAttribute("name", setProp.Name);
if (setProp.Property.SerializeAs.ToString() == "String")
{
newSetting.SetAttribute("serializeAs", "String");
}
else
{
newSetting.SetAttribute("serializeAs", "Xml");
}
// Append this node to the application settings node (<Appname.Properties.Settings>)
tmpNode.AppendChild(newSetting);
// Create an element under our named settings node, and assign it the value we are trying to save
XmlElement valueElement = xmlDoc.CreateElement("value");
if (setProp.Property.SerializeAs.ToString() == "String")
{
valueElement.InnerText = setProp.SerializedValue.ToString();
}
else
{
// Write the object to the config serialized as Xml - we must remove the Xml declaration when writing
// the value, otherwise .Net's configuration system complains about the additional declaration
valueElement.InnerXml = setProp.SerializedValue.ToString().Replace(@"<?xml version=""1.0"" encoding=""utf-16""?>", "");
}
//Append this new element under the setting node we created above
newSetting.AppendChild(valueElement);
}
}
}
Giganews – The Best Usenet Provider, Hands Down



Thanks or this precious information, you solved a big problem with them
[...] admits they will not fix. [Mike] from Geek Republic has taken it upon himself to show us how to hack up some code to make your programs portable. This code is a good push forward for people loving the portability of modern applications. He [...]
Why didn’t you use Linq to XML, read/write is much less time consuming and (in my opinion) it’s easier to use. Unless of course you wish to support .NET 2.0
Less time consuming as in it uses less CPU cycles when reading/writing XML, or less time consuming as far as programming is concerned?
It looks interesting enough, I suppose I’ll give it a go. I am by no means a professional developer, I’m just a guy who writes apps to get things done in my day to day life. Any pointers (this one included) are always welcome.
Hi!
Thanks for this excellent code!! However I have a small problem. When I install my application, and starts my application as a user (not Administrator) I cannot get write/modify access to the config file… If I manually change the permissions of the file it works perfect….
I’ve tried googling for a couple of hours but haven’t found any way I can programmatically change access permissions for the specific .config file.
Please help,
Thanks!
Kaare,
Are you using the settings provider for a portable application, or for one that installs under the Program Files directory?
My guess is that if the program is in the Program Files directory, you are running into permissions issues due to UAC. If I recall correctly, UAC locks down the Program Files directory as it is considered a “special” directory by Windows. If you move your files to a folder in the root of your C:\ drive, do you see the same behavior?
You could always work in Windows’ UAC functionality by implementing a manifest in Visual Studio that is configured to require the user to elevate the application’s permissions. This could remedy the issue if the program is installed under the Program Files directory.
Hi Mike,
Thanks for the class. However, I came across problems when using in my settings ArrayList or StringCollection. The cast to string[] during deserialization was throwing exception. When I sorted out the type cast it still didn’t work because the code was assigning object of ArrayList or StringCollection to SettingsPropertyValue SerializedValue. What was needed was the serialized object instead of deserialized.
So in GetSetting(SettingsProperty) instead of the following:
// If the setting is stored as XML, deserialize it and return the proper object. This only supports
// StringCollections at the moment – I will likely add other types as I use them in applications.
else
{
string settingType = setProp.PropertyType.ToString();
string xmlData = XMLConfig.SelectSingleNode(“//setting[@name='" + setProp.Name + "']“).FirstChild.InnerXml;
XmlSerializer xs = new XmlSerializer(typeof(string[]));
string[] data = (string[])xs.Deserialize(new XmlTextReader(xmlData, XmlNodeType.Element, null));
switch (settingType)
{
case “System.Collections.Specialized.StringCollection”:
StringCollection sc = new StringCollection();
sc.AddRange(data);
return sc;
default:
return “”;
}
}
all that was needed was:
else
{
string xmlData = XMLConfig.SelectSingleNode(“//setting[@name='" + setProp.Name + "']“).FirstChild.InnerXml;
return @”" + xmlData;
}
It took me whole morning to come to this. This is much simpler and leaves the whole deserializing to .Net classes. It makes it much more generic as any settings type that is valid in .Net can be used.
All the best,
Ewa
very useful, thanks
Ewa, that’s fantastic!
It solved a continued problem I had with retrieving StringCollections from the config file. Thanks!
[...] geworden bin ich bei Mike Nathan auf Geek Republic. Sein C# Portable Settings Provider macht genau das. Da ich meine Settings nicht unter Properties [...]
Hey, very nice!
But… I get an “System.NullReferenceException” when accessing settings which contain a bool value (even when not accessing the bool value itself).
What am I doing wrong? Thank you!
How to set ‘PortableSettingsProvider’ in application’s settings in VS 2010?? I am not getting any way to do that there.
Once you have added the PortableSettingsProvider source file to your project (Right-click your project name, select “Add->Existing Item”, it’s fairly easy to use.
You need to right-click on your project name, and select “Properties”. Once the properties pane appears, select the “Settings” tab. You can select all of your application’s settings, then simply type “PortableSettingsProvider” in the properties window in VS2010. I always have the properties window open when I use VS2010, but you may need to enable it first.
Hi Mike,
This is really useful — but you need to add it to each application as is. I prefer library code – but you need to make the settings specific to the running application somehow. And really, storing user settings under Program Files is not a good move.
So here’s an amended version, with doc-comments, too.
/*************************************************************
*
* Copyright (C) 2011 by Peter L Jones
* pljones@users.sf.net
*
* This is a derived work from:
*
* PortableSettingsProvider.cs
* Portable Settings Provider for C# applications
*
* 2010- Michael Nathan
* http://www.Geek-Republic.com
*
* Licensed under Creative Commons CC BY-SA
* http://creativecommons.org/licenses/by-sa/3.0/legalcode
*
*************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace System.Configuration
{
///
/// Provides persistence for application settings classes without the random folder naming of .
///
public class PortableSettingsProvider : SettingsProvider//, IApplicationSettingsProvider
{
#region Template XML
/* Define some static strings later used in our XML creation */
// XML Root node
private const string XMLROOT = “configuration”;
// Configuration declaration node
private const string CONFIGNODE = “configSections”;
// Configuration section group declaration node
private const string GROUPNODE = “sectionGroup”;
// User section node
private const string USERNODE = “userSettings”;
// Application Specific Node
private static string APPNODE = System.Diagnostics.Process.GetCurrentProcess().ProcessName + “.Properties.Settings”;
private System.Xml.XmlDocument xmlDoc = null;
private static System.Xml.XmlDocument _xmlDocTemplate
{
get
{
System.Xml.XmlDocument _xmlDoc = new XmlDocument();
// XML Declaration
//
XmlDeclaration dec = _xmlDoc.CreateXmlDeclaration(“1.0″, “utf-8″, null);
_xmlDoc.AppendChild(dec);
// Create root node and append to the document
//
XmlElement rootNode = _xmlDoc.CreateElement(XMLROOT);
_xmlDoc.AppendChild(rootNode);
// Create Configuration Sections node and add as the first node under the root
//
XmlElement configNode = _xmlDoc.CreateElement(CONFIGNODE);
_xmlDoc.DocumentElement.PrependChild(configNode);
// Create the user settings section group declaration and append to the config node above
//
XmlElement groupNode = _xmlDoc.CreateElement(GROUPNODE);
groupNode.SetAttribute(“name”, USERNODE);
groupNode.SetAttribute(“type”, “System.Configuration.UserSettingsGroup”);
configNode.AppendChild(groupNode);
// Create the Application section declaration and append to the groupNode above
//
XmlElement newSection = _xmlDoc.CreateElement(“section”);
newSection.SetAttribute(“name”, APPNODE);
newSection.SetAttribute(“type”, “System.Configuration.ClientSettingsSection”);
groupNode.AppendChild(newSection);
// Create the userSettings node and append to the root node
//
XmlElement userNode = _xmlDoc.CreateElement(USERNODE);
_xmlDoc.DocumentElement.AppendChild(userNode);
// Create the Application settings node and append to the userNode above
//
XmlElement appNode = _xmlDoc.CreateElement(APPNODE);
userNode.AppendChild(appNode);
return _xmlDoc;
}
}
#endregion
///
/// Initializes the provider.
///
/// The friendly name of the provider.
/// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.
/// The name of the provider is null.
/// The name of the provider has a length of zero.
/// An attempt is made to call System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection) on a provider after the provider has already been initialized.
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(this.ApplicationName, config);
}
private static string _ApplicationName = null;
///
/// Return the executing assembly name without extension.
///
public override string ApplicationName
{
get
{
if (_ApplicationName == null)
_ApplicationName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
return _ApplicationName;
// return (System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}
set { if (_ApplicationName == null) _ApplicationName = value; }
}
///
/// Provide the application settings filename.
///
/// The application settings filename.
public virtual string GetSettingsFilename() { return Path.Combine(GetSettingsPath(), ApplicationName + “.user.config”); }
private static string _SettingsPath = null;
///
/// Provide the settings location for user settings.
///
/// The settings location for user settings.
/// The settings location is “%ApplicationData%\[AssemblyCompany]\getApplicationName()”.
public virtual string GetSettingsPath()
{
if (_SettingsPath == null)
{
object[] conames = this.GetType().Assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyCompanyAttribute), false);
string coname = conames.Length == 1 ? ((System.Reflection.AssemblyCompanyAttribute)conames[0]).Company : “noCompany”;
_SettingsPath = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), coname), ApplicationName);
}
return _SettingsPath;
}
///
/// Retrieve settings from the configuration file.
///
/// Provides contextual information that the provider can use when persisting settings.
/// Contains a collection of objects.
/// A collection of settings property values that map objects to objects.
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext sContext, SettingsPropertyCollection settingsColl)
{
// Create a collection of values to return
SettingsPropertyValueCollection retValues = new SettingsPropertyValueCollection();
// Create a temporary SettingsPropertyValue to reuse
SettingsPropertyValue setVal;
// Loop through the list of settings that the application has requested and add them
// to our collection of return values.
foreach (SettingsProperty sProp in settingsColl)
{
setVal = new SettingsPropertyValue(sProp);
setVal.IsDirty = false;
setVal.SerializedValue = GetSetting(sProp);
retValues.Add(setVal);
}
return retValues;
}
///
/// Save any of the applications settings that have changed (flagged as “dirty”).
///
/// Provides contextual information that the provider can use when persisting settings.
/// Contains a collection of objects.
/// The operation would not result in a well formed XML document (for example, no document element or duplicate XML declarations).
public override void SetPropertyValues(SettingsContext sContext, SettingsPropertyValueCollection settingsColl)
{
// Set the values in XML
foreach (SettingsPropertyValue spVal in settingsColl)
SetSetting(spVal);
if (!Directory.Exists(GetSettingsPath()))
Directory.CreateDirectory(GetSettingsPath());
XMLConfig.Save(GetSettingsFilename());
}
private XmlDocument XMLConfig
{
get
{
// Check if we already have accessed the XML config file. If the xmlDoc object is empty, we have not.
if (xmlDoc == null)
{
if (File.Exists(GetSettingsFilename()))
{
try
{
xmlDoc = new XmlDocument();
xmlDoc.Load(GetSettingsFilename());
}
catch
{
xmlDoc = (XmlDocument)_xmlDocTemplate.Clone();
}
}
else
{
xmlDoc = (XmlDocument)_xmlDocTemplate.Clone();
}
}
return xmlDoc;
}
}
// Retrieve values from the configuration file, or if the setting does not exist in the file,
// retrieve the value from the application’s default configuration
private object GetSetting(SettingsProperty setProp)
{
object retVal;
try
{
// Search for the specific settings node we are looking for in the configuration file.
// If it exists, return the InnerText or InnerXML of its first child node, depending on the setting type.
// If the setting is serialized as a string, return the text stored in the config
if (setProp.SerializeAs.ToString() == “String”)
{
return XMLConfig.SelectSingleNode(“//setting[@name='" + setProp.Name + "']“).FirstChild.InnerText;
}
// If the setting is stored as XML, deserialize it and return the proper object.
else
{
string xmlData = XMLConfig.SelectSingleNode(“//setting[@name='" + setProp.Name + "']“).FirstChild.InnerXml;
return @”" + xmlData;
}
}
catch (Exception)
{
// Check to see if a default value is defined by the application.
// If so, return that value, using the same rules for settings stored as Strings and XML as above
if ((setProp.DefaultValue != null))
{
if (setProp.SerializeAs.ToString() == “String”)
{
retVal = setProp.DefaultValue.ToString();
}
else
{
string settingType = setProp.PropertyType.ToString();
string xmlData = setProp.DefaultValue.ToString();
XmlSerializer xs = new XmlSerializer(typeof(string[]));
string[] data = (string[])xs.Deserialize(new XmlTextReader(xmlData, XmlNodeType.Element, null));
switch (settingType)
{
case “System.Collections.Specialized.StringCollection”:
StringCollection sc = new StringCollection();
sc.AddRange(data);
return sc;
default: return “”;
}
}
}
else
{
retVal = “”;
}
}
return retVal;
}
private void SetSetting(SettingsPropertyValue setProp)
{
// Define the XML path under which we want to write our settings if they do not already exist
XmlNode SettingNode = null;
try
{
// Search for the specific settings node we want to update.
// If it exists, return its first child node, (the data here node)
SettingNode = XMLConfig.SelectSingleNode(“//setting[@name='" + setProp.Name + "']“).FirstChild;
}
catch (Exception)
{
SettingNode = null;
}
// If we have a pointer to an actual XML node, update the value stored there
if ((SettingNode != null))
{
if (setProp.Property.SerializeAs.ToString() == “String”)
{
SettingNode.InnerText = setProp.SerializedValue.ToString();
}
else
{
// Write the object to the config serialized as Xml – we must remove the Xml declaration when writing
// the value, otherwise .Net’s configuration system complains about the additional declaration.
SettingNode.InnerXml = setProp.SerializedValue.ToString().Replace(@”", “”);
}
}
else
{
// If the value did not already exist in this settings file, create a new entry for this setting
// Search for the application settings node () and store it.
XmlNode tmpNode = XMLConfig.SelectSingleNode(“//” + APPNODE);
// Create a new settings node and assign its name as well as how it will be serialized
XmlElement newSetting = xmlDoc.CreateElement(“setting”);
newSetting.SetAttribute(“name”, setProp.Name);
if (setProp.Property.SerializeAs.ToString() == “String”)
{
newSetting.SetAttribute(“serializeAs”, “String”);
}
else
{
newSetting.SetAttribute(“serializeAs”, “Xml”);
}
// Append this node to the application settings node ()
tmpNode.AppendChild(newSetting);
// Create an element under our named settings node, and assign it the value we are trying to save
XmlElement valueElement = xmlDoc.CreateElement(“value”);
if (setProp.Property.SerializeAs.ToString() == “String”)
{
valueElement.InnerText = setProp.SerializedValue.ToString();
}
else
{
// Write the object to the config serialized as Xml – we must remove the Xml declaration when writing
// the value, otherwise .Net’s configuration system complains about the additional declaration
valueElement.InnerXml = setProp.SerializedValue.ToString().Replace(@”", “”);
}
//Append this new element under the setting node we created above
newSetting.AppendChild(valueElement);
}
}
}
}
Peter,
Thanks for the feedback and additions to the code. I merely program as a hobby or to automate tasks for myself, so any tips are welcome. I’ll look into making this library code, as I have no idea what that entails offhand.
I do see your point regarding the storage of settings in the program files folder, though the aim for this code is to enable application portability. As someone who frequently uses flash drives in my work, I like the idea of having an application and its settings stored there so that I don’t have to reconfigure my options each and every time I fire it up on a different machine.