Search code examples
c#xmlapp-config

C# Windows Form: pragmatically edit/update an Array of strings in resulting App.Config file


I need to pragmatically update an auto generated app.config file of a Windows Form.

Specifically an ArrayOfStrings.

Desired out put:

     <setting name="Test" serializeAs="Xml">
        <value>
           <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
              <string>Test 1</string>
              <string>Test 2</string>
              <string>Test 3</string>
           </ArrayOfString>
        </value>
     </setting>

What my code generates:

     <setting name="Test" serializeAs="Xml">
        <value>New Data 3</value>
     </setting>

I have researched Xml serialization, but it is completely new to me and I am struggling to figure it out.

public void saveCardDataToSettings() {

List<String> DetectorCardTypeData = new List<string>();
foreach (DataGridViewRow row in dgvDetectorCardData.Rows)
{
    string CurrentRowData = (row.Cells[0].Value + "," + row.Cells[2].Value + "," + row.Cells[4].Value);
    DetectorCardTypeData.Add(CurrentRowData);
}

StringCollection TestData = new StringCollection();
foreach (string CardData in DetectorCardTypeData)
{
    TestData.Add(CardData);
}

//Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSectionGroup applicationSectionGroup = config.GetSectionGroup("applicationSettings");
ConfigurationSection applicationConfigSection = applicationSectionGroup.Sections["EXP_Battery_Calculator.Properties.Settings"];
ClientSettingsSection clientSection = (ClientSettingsSection)applicationConfigSection;

SettingElement appData = clientSection.Settings.Get("Test");

foreach (string WriteME in TestData)
{
    appData.Value.ValueXml.InnerText = WriteME;

    clientSection.Settings.Add(appData);

}

applicationConfigSection.SectionInformation.ForceSave = true;
config.Save();


listBox1.DataSource = TestData;

}


Solution

  • Still can't understand why need to store data in the application config, but anyway.

    First of all, create a new parameter in your Settings file. Name it as you want ("StoredData" in example below), set type to System.Collections.Specialized.StringCollections, set scope "Application" and add default value (i've added "Empty"). This should produce an XML string:

    <?xml version="1.0" encoding="utf-16"?>
    <ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <string>Empty</string>
    </ArrayOfString>
    

    and also will modify default config:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <configSections>
            <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
                <section name="YourNamespace.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
            </sectionGroup>
        </configSections>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
        </startup>
        <applicationSettings>
            <YourNamespace.Properties.Settings>
                <setting name="StoredData" serializeAs="Xml">
                    <value>
                        <ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                            <string>Empty</string>
                        </ArrayOfString>
                    </value>
                </setting>
            </YourNamespace.Properties.Settings>
        </applicationSettings>
    </configuration>
    

    To edit ArrayOfString node i've used this code:

     // Diving through config nodes to <value> element
     var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    
     var appSettings = config.SectionGroups.Get("applicationSettings");
     if (appSettings == null)
         return;
    
     var settingsSection = appSettings.Sections.OfType<ClientSettingsSection>()
                          .FirstOrDefault(section => section.Settings.Get(nameof(Properties.Settings.Default.StoredData)) != null);
     if (settingsSection == null)
         return;
    
     var storedData = settingsSection.Settings.Get(nameof(Properties.Settings.Default.StoredData))?.Value?.ValueXml;
     if (storedData == null)
         return;
    
     // Removing everyting from <ArrayOfString> (including it)
     storedData.RemoveAll();
    
     // Initializing XmlDocument instance to create new nodes
     var xDoc = new XmlDocument();
    
     // Manually creating parent node <ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
     var xArrayOfString = xDoc.CreateElement("ArrayOfString");
     xArrayOfString.SetAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
     xArrayOfString.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    
     // Just test collection of strings
     var testData = Enumerable.Range(0, 15).Select(x => x.ToString());
    
     // Looping through records of DataGridView, which should already been "stringified"
     // Creating nodes <string>...</string> where "..." is string representation of each DataGridView row (in a way to want to store it)
     foreach (var testDataItem in testData)
     {
         var xString = xDoc.CreateElement("string");
         xString.InnerText = testDataItem;
         xArrayOfString.AppendChild(xString);
     }
    
    // And appending new rebuilded <ArrayOfString> to a <value> node in config
    storedData.AppendChild(storedData.OwnerDocument.ImportNode(xArrayOfString, true));
    
    // And just saving
    config.Save(ConfigurationSaveMode.Minimal, true);
    

    That example, after config.Save call immediately rewrites config and produces such look:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <configSections>
            <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
                <section name="YourNamespace.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
            </sectionGroup>
        </configSections>
        <startup>
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
        </startup>
        <applicationSettings>
            <YourNamespace.Properties.Settings>
                <setting name="StoredData" serializeAs="Xml">
                    <value>
                        <ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                            <string>0</string>
                            <string>1</string>
                            <string>2</string>
                            <string>3</string>
                            <string>4</string>
                            <string>5</string>
                            <string>6</string>
                            <string>7</string>
                            <string>8</string>
                            <string>9</string>
                        </ArrayOfString>
                    </value>
                </setting>
            </YourNamespace.Properties.Settings>
        </applicationSettings>
    </configuration>
    

    BUT it doesn't updates Property.Settings.Default.StoredData property at runtime. Only after application restart it will load modified data from config. It also doesn't modifies "App.config" in your project folder. Only "YourAssemblyName.exe.config" near your output binary file.

    Not sure this way is appropriate or applicable. Still didn't understand why you decided to punish little config file in such way. You have user-scoped settings for that. Or just write own separate file such as "myStoredData.xml" in application directory.