Preface
I know, the concept of app.config files is not part of Windows Store App development. The docs and postings say that one should use local or roaming settings.
But I needed something to pass environment depending initial data to the app. One scenario I can think of is the URI of a web service that is consumed by an app. This URI might defer from development environment to test to production in case you develop both the app and the service. Changing the code or using conditional directive (#if) might not be the best idea to implement that.
appSettings Approach
The approach copies the appSettings part of .NET app.config usage. Just add an XML file to the project and make sure the “Build Action” property of the file is set to “Content”. This file will be included into the deployment package and can be accessed during runtime.
The content of this file might look like this:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="MyConfigSetting" value="The Config Value"/> </appSettings> </configuration>
Since app.config is not part of the Windows Store app concept, there is no ConfigurationManager
class to ease the access. But it is not that hard to read and parse XML files.
In my approach, I implemented the loading of the XML file in two steps: get the file and load it.
StorageFile file = await StorageFile .GetFileFromApplicationUriAsync( new Uri(String.Format("ms-appx:///Assets/Configuration/{0}", ConfigManager.ConfigFileName))); XmlDocument xmlConfiguration = await XmlDocument.LoadFromFileAsync(configFile);
As you can see from the URI, I put the config file into the Assets/Confioguration folder. The name is dynamic to give me the chance to use different files for different environments. I will come back to this later.
If you know the Windows.Data.Xml.Dom.XmlDocument
class you might ask why I am not using the XmlDocument.LoadFromUriAsync
method. The answer is simple: it did not work right away (using the same URI), and I was too lazy to search for the reason.
Now that the XML is loaded, it is easy to read the values:
IXmlNode node = xmlConfiguration.DocumentElement .SelectSingleNode( "./appSettings/add[@key='MyConfigSetting']/@value"); if (node == null) { // do some error handling or set default value return; } string configValue = (string) node.NodeValue;
And that’s it. One might want to encapsulate this functionality by a class, implementing additional error handling, and so forth.
Complex Configuration Data using XML Serialization
In .NET client applications, one can use sections to handle complex configuration data that does not fit into key / value pairs. Of course, even for .NET client applications there is a good chance you have to write the section handler yourself.
For Windows Store apps, it is sure you have to write the handling of complex configuration data yourself. But you don’t have write loads of XML readings. Why? Because you can use XML serialization, like in .NET applications. The System.Xml.Serialization.XmlSerializer
is available for Windows Store apps too 🙂 Well, at least almost. Not all members available in .NET are available for Windows Store apps. But it became a lot easier to deserialize objects from XML without namespace qualification. (More about this issue for .NET can be found here: http://stackoverflow.com/questions/870293/can-i-make-xmlserializer-ignore-the-namespace-on-deserialization). There is no helper needed any more (at leats for Windows Store apps).
So what have I done? I created a container class for the complex configuration data. This class might look like this (comments omitted):
public class ComplexConfigData { [XmlAttribute] public string StringValue { get; set; } [XmlAttribute] public double DoubleValue { get; set; } [XmlAttribute] public int IntValue { get; set; } }
I am using the XmlAttribute attribute to keep the XML compact. The config file with additional complex data looks like this:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="MyConfigSetting" value="The Config Value"/> </appSettings> <ComplexConfigDataList> <ComplexConfigData StringValue='StringValue1' DoubleValue='1.1' IntValue='10'/> <ComplexConfigData StringValue='StringValue2' DoubleValue='2.2' IntValue='20'/> </ComplexConfigDataList> </configuration>
Here is how all the (manually) serialized ComplexConfigData objects are retrieved from the XML:
XmlNodeList configDataList = xmlConfiguration.DocumentElement .SelectNodes( "./ComplexConfigDataList/ComplexConfigData"); foreach (IXmlNode node in configDataList) { ComplexConfigData configData = XmlDeserializer.Deserialize<ComplexConfigData>(node); // do something with configData }
The deserialization is encapsulated by this generic method in my class XmlDeserializer
:
public static T Deserialize<T> ( IXmlNode serializedObject ) where T : class { return ( new XmlSerializer(typeof(T)) .Deserialize( new StringReader( serializedObject.GetXml())) as T); }
Selecting the Correct File
Above, where the URI is built to get the config file, a placeholder and a property is used. The property is defined as follows:
private const string ConfigFileName #if DEBUG = "Config.Debug.xml"; #else = "Config.Release.xml"; #endif // DEBUG
The reason to use the conditional directive (#if) is that I do not want to change the code in case I have different settings for debug (development) and release (production). This approach does not fit for three different environments, but it is a starting point. Of course, you can change the content of the config file whenever you need. But this is more error prone than selecting the wrong build configuration. (The Windows Store only accepts release builds, so this approach will pick the release settings when the app is installed via Windows Store.)
Sample Variation
With version 3.0 of the source code, the sample implementation is not exactly what is described here. For demonstration purposes I thought it would be nice to be able to switch the app.config that is used via the Settings charm.
This means, the name of the app.config file loaded does not depend on the build configuration, but on app configuration values. You can switch it directly. The change is reflected immediately by the UI controls.
As said, this is for demonstration purposes. I do not think that this approach (giving the user the ability to switch the app.config) will be very useful in real world scenarios. But for this sample I think it is.
In case you want to use the app.config mimic in your app, the code to couple the app.config to the build configuration is still included in the sample. See Configuration.ConfigurationManager
.
Considerations
Think twice if you really need this. Don’t use it just because you are used to it from .NET applications.
If you have the need to save configuration data during runtime, the app.config mimic is not the right approach to me. Better use local or roaming application data then (see links below).
Load It From The Windows Store
A slightly extented version of the sample can be found in the Windows Store.
Finally
… have fun 🙂
You can find the sample code here.
History
2013-08-13: Source code update V3.0.0.1
Instance Factory Logo changed; typo fixed.
2013-08-09: Source code update V3.0
Select the app.config file via Settings Charm, independend from build configuration.
2013-07-23: Source code update V2.1
Minor design changes (logo & splash screen).
2013-07-10: Source code update V2.0
UI completely redesigned.
Links
Local application data (Windows Store apps using C#/VB/C++ and XAML)
Roaming application data (Windows Store apps using C#/VB/C++ and XAML)
How to load file resources (Windows Store apps using C#/VB/C++ and XAML)
Guidelines for app settings (Windows Store apps)
Implement Settings Popup Pages for Windows Store Apps sample