Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Using XAML for Custom Application Configuration

3.74/5 (15 votes)
16 Apr 2008CPOL8 min read 1  
Describes why and how to use XAML as an alternative to plain XML for configuration file specification

Introduction

A popular pattern for application design and development is to use offline files to persist configuration information so that downstream users can modify certain properties associated with an application without requiring rebuild activities like SDLC. When I was a Java Architect for Sun MicroSystems, the Properties file was the prescribed approach to do this. Although it offered some flexibility, it certainly was not ideal for many situations, especially those requiring hierarchical structures. With the advent of .NET, Microsoft opted for the much more robust and powerful extensible markup language as the underlying mechanism for storage of the offline configuration parameters. This works out great. XML is a perfect solution for such scenarios. It’s relatively simple to implement, fast, yet manages to be very powerful, extensible, and flexible.

As great as XML is however, there are some shortcomings. In the case where the configuration file is a custom one defined outside the realm of the canned configuration files like app.config or web.config by Microsoft, the problem of schema validation and synchronization can be an added headache, especially since they are generally an unnecessary effort. What I mean is, these tasks have nothing to do with the actual configuration of the application but tend to be thought about a lot. In the case of the canned config files, there is a general lack of intuitive comprehension to the config file formats. One simply has to know how to configure a WCF service for instance. There is no notion of being able to intuitively configure one from scratch without knowledge of the spelling and casing of every word/character typed. I must admit that you are helped along in many regards with the intellisense engine which seems to be built atop of schema specifications, but I found that for the canned config files it was still not adequate, and for the custom ones I didn't want to have to write a schema for my configuration.

Background

Now with the advent of .NET 3.0 comes the concept of XAML. Much more than just a presentational DSL, XAML is in fact a powerful notation for describing the runtime state of objects using XML. In some cases, it is the functional equivalent to describing this runtime behavior using, say, C# of VB or whatever language of choice you desire. Because of its very general approach to describing objects, XAML is used not just for presentational description (WPF) but also for describing workflows (WF). The last thing that gives XAML its amazing power is the fact that Microsoft introduced with it a framework for dynamically loading it on the fly! What does this mean to the average man? It means that pure XAML does not have to be compiled to work; it can be read in at runtime and still generate the appropriate object graphs. Think how JavaScript can be used to produce powerful and dynamic runtime behavior like json because of the eval function. (Of course, JavaScript offers a much more robust implementation since the entire language is late bound. Please give us dynamic programming C# team!)
What should be clear from all this is that XAML is the perfect choice for, and I believe next evolution of, offline configuration (among other cool things beyond the scope of this article). This series of articles describes in detail and with examples how to setup and utilize XAML as a configuration file or as a supplement to the standard configuration files. It also includes an XAML config section library for attaching XAML to your preexisting app.config or web.config files.

The Process

Without the cloud of WPF or WF to confuse us, most people would agree that XAML by itself is a very simple mechanism for representing objects. Indeed ANY accessible class can be represented in object form as XAML. Here is the representation of a .NET string in XAML:

XML
<String xmlns=""clr-namespace:System;assembly=mscorlib"" >hello world</String> 

Reading this value into your application involves three simple things.

First, the application must reference presentationcore and presentationframework. This is a minor setback that I'm sure someone will rectify at some point.

Secondly, now that we have access to XamlReader and XamlWriter (found in the namespace system.windows.markup), we can use the Load and Save methods to convert the XAML to the instance it represents or convert an instance to its XAML representation respectively. Finally, all that’s left is designing a new class to support all our configuration needs (try to make it serializable) and representing the instance of that class in XAML notation. So let's go ahead and create a simple project to consume this string configuration. To start with, create a simple console application, and add references to presentationcore.dll and presentationframework.dll. Once you are done with that, add the following lines of code to the Main Method :

C#
string xaml = @"<string assembly=""mscorlib"" >
    <String xmlns=""clr-namespace:System;assembly=mscorlib"" >
    hello world</String></string >";
byte[] xaml_data = System.Text.Encoding.ASCII.GetBytes(xaml);
System.IO.MemoryStream xaml_stream = new System.IO.MemoryStream(xaml_data);
string xaml_object = (string)System.Windows.Markup.XamlReader.Load(xaml_stream);
Console.WriteLine(xaml_object);

Be sure to use the double quote notation if you are going to use a verbatim string like I have done. Running this code will produce the string hello world in the console window. There is one major thing to note here if you are not familiar with XAML as a whole. Just as in C# or any other .NET language where you must declare the namespaces you plan to use so that the compiler knows how to resolve the types you utilize in your listing, XAML, more specifically the xamlreader and xamlwriter classes, require you to specify a namespace to help resolve the elements you specify within the document . The attribute xmlns="clr-namespace:System;assembly=mscorlib" in this case specifies that the default namespace for the document will be the System namespace in the assembly mscorlib. So any type within that namespace (within the assembly) can be used here. Try out using different types within the namespace, changing namespaces and/or assemblies and also changing the values of the types you specify.

The first thing you should notice from the modifications you've been making is the need to recompile the code every single time in order to see the changes reflected. Obviously, this would not be ideal for a configuration file situation, so we will be moving the XAML out of the code base and into a flat file somewhere in the filesystem. As you might have noticed from the various overrides of the XamlReader’s Load function, it also accepts an XmlReader or a Stream. Either types can be used here as a mechanism to read in the XAML file. The code below shows an implementation that reads the sample file config.xaml in the same directory as the executable.

Note: There is presently no way to add an XAML configuration file into a Visual Studio project so you will have to improvise. You can add a simple text file and then change the file extension to XAML or you can add a standard XAML file (for WinForm applications, this is currently a UserControl) then modify it as needed. If you choose the second approach, be sure to remove the code behind file associated with the XAML file; also change the build action for the file to None (found in the properties window while the file is selected), and delete the text in the Custom Tool property item. As there is no penalty to starting from a blank file, I suggest using that over the latter approach.

The resulting code is listed below:

C#
using (FileStream xaml_stream = File.OpenRead("config.xaml"))
{
string xaml_object = (string)System.Windows.Markup.XamlReader.Load(xaml_stream);
Console.WriteLine(xaml_object);
}

Now that we've successfully illustrated that we can indeed read types in from an external XAML file and process it within a host application, the next step is to create an actual configuration file rather than use built-in .NET types. In the old days of simple XML config files, you designed your schema (or just started slinging XML) and then built a dedicated type that knew how to read that schema and that was that. I believe it somewhat evolved to using the XML serializer to essentially represent pre-defined types in XML form. This approach is relatively similar to that, but focuses more on the XML (XAML) than on the actual classes behind them. More importantly though, XAML offers a far more flexible and robust mechanism for describing objects than the XML serializer generated XML does.

The first step is to create a class that will represent the configuration. The listing below represents the configuration for a LaunchPoint, an open source project on Codeplex and Sourceforge aimed at creating a unified and simplified framework for building secure smartclient applications on top of WPF. If you've worked with WCF, this will look a little like how data contracts are composed.

C#
namespace MindFactorial.LaunchPoint.Runtime
{
[Serializable]
public class LaunchPointConfiguration
{
public bool LaunchInDefaultContext { get; set; }
public LaunchPointApp Application { get; set; }
public PluginConfigList Plugins { get; set; }


public override string ToString()
{
string xml = XamlWriter.Save(this);
return xml;
}

}

[Serializable]
public class LaunchPointApp
{
public string Path { get; set; }
public string Name { get; set; }
public string RunnableTypeName { get; set; }
}

[Serializable]
public class PluginConfig
{
public string Name { get; set; }
public string Path { get; set; }
public string Label { get; set; }
}

[Serializable]
public class PluginConfigList : List<pluginconfig /> { }

}

The namespace name is included here because, as we've seen earlier, it is essential to the XamlReader when loading the XAML into the appropriate object. It should be noted that any invalid XAML or undiscoverable type will result in a parser exception. This is actually a good thing because it gives us schema like validation of the configuration file without the need to create a schema. Using the patterns and practices described above, this class can be represented in XAML as follows:

XML
<LaunchPointConfiguration
xmlns="clr-namespace:MindFactorial.LaunchPoint.Runtime;
    assembly=MindFactorial.LaunchPoint.Runtime"
>
<LaunchPointConfiguration.Application>
<LaunchPointApp>
<LaunchPointApp.Name>Console</LaunchPointApp.Name>
<LaunchPointApp.Path>MindFactorial.LaunchPoint.Console.dll</LaunchPointApp.Path>
<LaunchPointApp.RunnableTypeName>MindFactorial</LaunchPointApp.RunnableTypeName>
</LaunchPointApp>
</LaunchPointConfiguration.Application>
<LaunchPointConfiguration.Plugins>
<PluginConfigList>
<PluginConfig>
<PluginConfig.Name>one</PluginConfig.Name>
<PluginConfig.Label>first plugin</PluginConfig.Label>
<PluginConfig.Path>debug\generalsettings.plugin.dll</PluginConfig.Path>
</PluginConfig>
</PluginConfigList>
</LaunchPointConfiguration.Plugins>
</LaunchPointConfiguration>

As you can see from the namespace declarations, processing this file requires the processor to have a reference to the assembly MindFactorial.LaunchPoint.Runtime. This is the assembly where the type LaunchPointConfiguration is specified. If this file is placed in the same assembly as the XAML loading application, then the assembly portion of the namespace declaration can be ignored.

History

  • 16th April, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)