Application not utilizing custom SettingsProvider for StringCollection

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
I have put together a custom SettingsProvider in order to generate config files in the same directory from which my application is running, rather than using Microsofts specified paths.
When the application initializes, I can see that my SettingsProvider is being queried for settings, as I have set a quick MessageBox to show me each time the SettingsProvider is being queried for a value. This includes the StringCollection object I
am trying to retrieve.
When I save any settings, I can see that the SettingsProvider is being used for writing those values to disk as the configuration file is being changed.
The problem arises however, if I try to read the StringCollection from my settings file at any later point while the application is running. From what I understand, the proper syntax to obtain these values is Properties.Settings.Default.<settingname>,
provided I have selected my SettingsProvider as the provider in the Visual Studio Settings grid.
I swear that the code was working at some point, but perhaps it was my imagination. It seems to work with any other settings such as strings, booleans, and integers - just not my StringCollection. If I put a breakpoint in the code at the "GetPropertyValues"
method, It is never reached when attempting to retrieve a StringCollection.
The code is as follows:
<div style="color:Black; background-color:White
<pre><span style="color:Blue using System;
<span style="color:Blue using System.Collections.Generic;
<span style="color:Blue using System.Collections.Specialized;
<span style="color:Blue using System.Configuration;
<span style="color:Blue using System.Linq;
<span style="color:Blue using System.Text;
<span style="color:Blue using Microsoft.Win32;
<span style="color:Blue using System.Xml;
<span style="color:Blue using System.Xml.Serialization;

<span style="color:Blue public <span style="color:Blue class PortableSettingsProvider : SettingsProvider
{
<span style="color:Green // Define some static strings later used in our XML creation
<span style="color:Green // XML Root node
<span style="color:Blue const <span style="color:Blue string XMLROOT = <span style="color:#A31515 "configuration";

<span style="color:Green // Configuration declaration node
<span style="color:Blue const <span style="color:Blue string CONFIGNODE = <span style="color:#A31515 "configSections";

<span style="color:Green // Configuration section group declaration node
<span style="color:Blue const <span style="color:Blue string GROUPNODE = <span style="color:#A31515 "sectionGroup";

<span style="color:Green // User section node
<span style="color:Blue const <span style="color:Blue string USERNODE = <span style="color:#A31515 "userSettings";

<span style="color:Green // Application Specific Node
<span style="color:Blue string APPNODE = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + <span style="color:#A31515 ".Properties.Settings";

<span style="color:Blue private System.Xml.XmlDocument xmlDoc = <span style="color:Blue null;



<span style="color:Green // Override the Initialize method
<span style="color:Blue public <span style="color:Blue override <span style="color:Blue void Initialize(<span style="color:Blue string name, NameValueCollection config)
{
<span style="color:Blue base.Initialize(<span style="color:Blue this.ApplicationName, config);
}

<span style="color:Green // Override the ApplicationName property, returning the solution name. No need to set anything, we just need to
<span style="color:Green // retrieve information, though the set method still needs to be defined.
<span style="color:Blue public <span style="color:Blue override <span style="color:Blue string ApplicationName
{
<span style="color:Blue get
{
<span style="color:Blue return (System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}
<span style="color:Blue set
{
<span style="color:Blue return;
}
}

<span style="color:Green // Simply returns the name of the settings file, which is the solution name plus ".config"
<span style="color:Blue public <span style="color:Blue virtual <span style="color:Blue string GetSettingsFilename()
{
<span style="color:Blue return ApplicationName + <span style="color:#A31515 ".config";
}

<span style="color:Green // Gets current executable path in order to determine where to read and write the config file
<span style="color:Blue public <span style="color:Blue virtual <span style="color:Blue string GetAppPath()
{
<span style="color:Blue return <span style="color:Blue new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName;
}

<span style="color:Green // Retrieve settings from the configuration file
<span style="color:Blue public <span style="color:Blue override SettingsPropertyValueCollection GetPropertyValues(SettingsContext sContext, SettingsPropertyCollection settingsColl)
{
<span style="color:Green // Create a collection of values to return
SettingsPropertyValueCollection retValues = <span style="color:Blue new SettingsPropertyValueCollection();

<span style="color:Green // Create a temporary SettingsPropertyValue to reuse
SettingsPropertyValue setVal;

<span style="color:Green // Loop through the list of settings that the application has requested and add them
<span style="color:Green // to our collection of return values.
<span style="color:Blue foreach (SettingsProperty sProp <span style="color:Blue in settingsColl)
{
setVal = <span style="color:Blue new SettingsPropertyValue(sProp);
setVal.IsDirty = <span style="color:Blue false;
setVal.SerializedValue = GetSetting(sProp);
retValues.Add(setVal);
}
<span style="color:Blue return retValues;
}

<span style="color:Green // Save any of the applications settings that have changed (flagged as "dirty")
<span style="color:Blue public <span style="color:Blue override <span style="color:Blue void SetPropertyValues(SettingsContext sContext, SettingsPropertyValueCollection settingsColl)
{
<span style="color:Green // Set the values in XML
<span style="color:Blue foreach (SettingsPropertyValue spVal <span style="color:Blue in settingsColl)
{
SetSetting(spVal);
}

<span style="color:Green // Write the XML file to disk
<span style="color:Blue try
{
XMLConfig.Save(System.IO.Path.Combine(GetAppPath(), GetSettingsFilename()));
}
<span style="color:Blue catch (Exception ex)
{
<span style="color:Green // Create an informational message for the user if we cannot save the settings.
<span style="color:Green // Enable whichever applies to your application type.

<span style="color:Green // Uncomment the following line to enable a MessageBox for forms-based apps
System.Windows.Forms.MessageBox.Show(ex.Message, <span style="color:#A31515 "Error writting configuration file to disk", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);

<span style="color:Green // Uncomment the following line to enable a console message for console-based apps
<span style="color:Green //Console.WriteLine("Error writing configuration file to disk: " + ex.Message);
}
}

<span style="color:Blue private XmlDocument XMLConfig
{
<span style="color:Blue get
{
<span style="color:Green // Check if we already have accessed the XML config file. If the xmlDoc object is empty, we have not.
<span style="color:Blue if (xmlDoc == <span style="color:Blue null)
{
xmlDoc = <span style="color:Blue new XmlDocument();

<span style="color:Green // If we have not loaded the config, try reading the file from disk.
<span style="color:Blue try
{
xmlDoc.Load(System.IO.Path.Combine(GetAppPath(), GetSettingsFilename()));
}

<span style="color:Green // If the file does not exist on disk, catch the exception then create the XML template for the file.
<span style="color:Blue catch (Exception)
{
<span style="color:Green // XML Declaration
<span style="color:Green // <?xml version="1.0" encoding="utf-8"?>
XmlDeclaration dec = xmlDoc.CreateXmlDeclaration(<span style="color:#A31515 "1.0", <span style="color:#A31515 "utf-8", <span style="color:Blue null);
xmlDoc.AppendChild(dec);

<span style="color:Green // Create root node and append to the document
<span style="color:Green // <configuration>
XmlElement rootNode = xmlDoc.CreateElement(XMLROOT);
xmlDoc.AppendChild(rootNode);

<span style="color:Green // Create Configuration Sections node and add as the first node under the root
<span style="color:Green // <configSections>
XmlElement configNode = xmlDoc.CreateElement(CONFIGNODE);
xmlDoc.DocumentElement.PrependChild(configNode);

<span style="color:Green // Create the user settings section group declaration and append to the config node above
<span style="color:Green // <sectionGroup name="userSettings"...>
XmlElement groupNode = xmlDoc.CreateElement(GROUPNODE);
groupNode.SetAttribute(<span style="color:#A31515 "name", USERNODE);
groupNode.SetAttribute(<span style="color:#A31515 "type", <span style="color:#A31515 "System.Configuration.UserSettingsGroup");
configNode.AppendChild(groupNode);

<span style="color:Green // Create the Application section declaration and append to the groupNode above
<span style="color:Green // <section name="AppName.Properties.Settings"...>
XmlElement newSection = xmlDoc.CreateElement(<span style="color:#A31515 "section");
newSection.SetAttribute(<span style="color:#A31515 "name", APPNODE);
newSection.SetAttribute(<span style="color:#A31515 "type", <span style="color:#A31515 "System.Configuration.ClientSettingsSection");
groupNode.AppendChild(newSection);

<span style="color:Green // Create the userSettings node and append to the root node
<span style="color:Green // <userSettings>
XmlElement userNode = xmlDoc.CreateElement(USERNODE);
xmlDoc.DocumentElement.AppendChild(userNode);

<span style="color:Green // Create the Application settings node and append to the userNode above
<span style="color:Green // <AppName.Properties.Settings>
XmlElement appNode = xmlDoc.CreateElement(APPNODE);
userNode.AppendChild(appNode);
}
}
<span style="color:Blue return xmlDoc;
}
}

<span style="color:Green // Retrieve values from the configuration file, or if the setting does not exist in the file,
<span style="color:Green // retrieve the value from the applications default configuration
<span style="color:Blue private <span style="color:Blue object GetSetting(SettingsProperty setProp)
{
<span style="color:Blue object retVal;
<span style="color:Blue try
{
<span style="color:Green // Search for the specific settings node we are looking for in the configuration file.
<span style="color:Green // If it exists, return the InnerText or InnerXML of its first child node, depending on the setting type.

<span style="color:Green // If the setting is serialized as a string, return the text stored in the config
<span style="color:Blue if (setProp.SerializeAs.ToString() == <span style="color:#A31515 "String")
{
<span style="color:Blue return XMLConfig.SelectSingleNode(<span style="color:#A31515 "//setting[@name=" + setProp.Name + <span style="color:#A31515 "]").FirstChild.InnerText;
}

<span style="color:Green // If the setting is stored as XML, deserialize it and return the proper object. This only supports
<span style="color:Green // StringCollections at the moment - I will likely add other types as I use them in applications.
<span style="color:Blue else
{
<span style="color:Blue string settingType = setProp.PropertyType.ToString();
<span style="color:Blue string xmlData = XMLConfig.SelectSingleNode(<span style="color:#A31515 "//setting[@name=" + setProp.Name + <span style="color:#A31515 "]").FirstChild.InnerXml;
XmlSerializer xs = <span style="color:Blue new XmlSerializer(<span style="color:Blue typeof(<span style="color:Blue string[]));
<span style="color:Blue string[] data = (<span style="color:Blue string[])xs.Deserialize(<span style="color:Blue new XmlTextReader(xmlData, XmlNodeType.Element, <span style="color:Blue null));

<span style="color:Blue switch (settingType)
{
<span style="color:Blue case <span style="color:#A31515 "System.Collections.Specialized.StringCollection":
StringCollection sc = <span style="color:Blue new StringCollection();
sc.AddRange(data);
<span style="color:Blue return sc;
<span style="color:Blue default:
<span style="color:Blue return <span style="color:#A31515 "";
}
}
}
<span style="color:Blue catch (Exception)
{
<span style="color:Green // Check to see if a default value is defined by the application.
<span style="color:Green // If so, return that value, using the same rules for settings stored as Strings and XML as above
<span style="color:Blue if ((setProp.DefaultValue != <span style="color:Blue null))
{
<span style="color:Blue if (setProp.SerializeAs.ToString() == <span style="color:#A31515 "String")
{
retVal = setProp.DefaultValue.ToString();
}
<span style="color:Blue else
{
<span style="color:Blue string settingType = setProp.PropertyType.ToString();
<span style="color:Blue string xmlData = setProp.DefaultValue.ToString();
XmlSerializer xs = <span style="color:Blue new XmlSerializer(<span style="color:Blue typeof(<span style="color:Blue string[]));
<span style="color:Blue string[] data = (<span style="color:Blue string[])xs.Deserialize(<span style="color:Blue new XmlTextReader(xmlData, XmlNodeType.Element, <span style="color:Blue null));

<span style="color:Blue switch (settingType)
{
<span style="color:Blue case <span style="color:#A31515 "System.Collections.Specialized.StringCollection":
StringCollection sc = <span style="color:Blue new StringCollection();
sc.AddRange(data);
<span style="color:Blue return sc;

<span style="color:Blue default: <span style="color:Blue return <span style="color:#A31515 "";
}
}
}
<span style="color:Blue else
{
retVal = <span style="color:#A31515 "";
}
}
<span style="color:Blue return retVal;
}

<span style="color:Blue private <span style="color:Blue void SetSetting(SettingsPropertyValue setProp)
{
<span style="color:Green // Define the XML path under which we want to write our settings if they do not already exist
XmlNode SettingNode = <span style="color:Blue null;

<span style="color:Blue try
{
<span style="color:Green // Search for the specific settings node we want to update.
<span style="color:Green // If it exists, return its first child node, (the <value>data here</value> node)
SettingNode = XMLConfig.SelectSingleNode(<span style="color:#A31515 "//setting[@name=" + setProp.Name + <span style="color:#A31515 "]").FirstChild;
}
<span style="color:Blue catch (Exception)
{
SettingNode = <span style="color:Blue null;
}

<span style="color:Green // If we have a pointer to an actual XML node, update the value stored there
<span style="color:Blue if ((SettingNode != <span style="color:Blue null))
{
<span style="color:Blue if (setProp.Property.SerializeAs.ToString() == <span style="color:#A31515 "String")
{
SettingNode.InnerText = setProp.SerializedValue.ToString();
}
<span style="color:Blue else
{
<span style="color:Green // Write the object to the config serialized as Xml - we must remove the Xml declaration when writing
<span style="color:Green // the value, otherwise .Nets configuration system complains about the additional declaration.
SettingNode.InnerXml = setProp.SerializedValue.ToString().Replace(<span style="color:#A31515 @"<?xml version=""1.0"" encoding=""utf-16""?>", <span style="color:#A31515 "");
}
}
<span style="color:Blue else
{
<span style="color:Green // If the value did not already exist in this settings file, create a new entry for this setting

<span style="color:Green // Search for the application settings node (<Appname.Properties.Settings>) and store it.
XmlNode tmpNode = XMLConfig.SelectSingleNode(<span style="color:#A31515 "//" + APPNODE);

<span style="color:Green // Create a new settings node and assign its name as well as how it will be serialized
XmlElement newSetting = xmlDoc.CreateElement(<span style="color:#A31515 "setting");
newSetting.SetAttribute(<span style="color:#A31515 "name", setProp.Name);

<span style="color:Blue if (setProp.Property.SerializeAs.ToString() == <span style="color:#A31515 "String")
{
newSetting.SetAttribute(<span style="color:#A31515 "serializeAs", <span style="color:#A31515 "String");
}
<span style="color:Blue else
{
newSetting.SetAttribute(<span style="color:#A31515 "serializeAs", <span style="color:#A31515 "Xml");
}

<span style="color:Green // Append this node to the application settings node (<Appname.Properties.Settings>)
tmpNode.AppendChild(newSetting);

<span style="color:Green // Create an element under our named settings node, and assign it the value we are trying to save
XmlElement valueElement = xmlDoc.CreateElement(<span style="color:#A31515 "value");
<span style="color:Blue if (setProp.Property.SerializeAs.ToString() == <span style="color:#A31515 "String")
{
valueElement.InnerText = setProp.SerializedValue.ToString();
}
<span style="color:Blue else
{
<span style="color:Green // Write the object to the config serialized as Xml - we must remove the Xml declaration when writing
<span style="color:Green // the value, otherwise .Nets configuration system complains about the additional declaration
valueElement.InnerXml = setProp.SerializedValue.ToString().Replace(<span style="color:#A31515 @"<?xml version=""1.0"" encoding=""utf-16""?>", <span style="color:#A31515 "");
}

<span style="color:Green //Append this new element under the setting node we created above
newSetting.AppendChild(valueElement);
}
}
}


[/code]



Any thoughts or insight? I am tearing my hair out trying to nail down the problem.

View the full article
 
Back
Top