Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

XML based registry implementation for configuration settings

4.75/5 (5 votes)
22 Jan 2009CPOL4 min read 27.2K   357  
A component that stores application content to an XML file.

Sample Image

Introduction

I think everyone knows about this problem. You wrote a new application and needs the ability to store some content on the local disk. It's not very difficult to implement this with the .NET XML classes, but it is always the same code. I wrote a component for this task and probably it is useful for you, too.

This article introduces a new component that I call Registry. It can be used in any .NET 3.5 application, and only depends on the assemblies delivered with the .NET 3.5 Framework. Every registry starts with a root folder. This folder can contains values and new subfolders. Each folder/value is accessible through a key. A value supports the following types:

  • string
  • int
  • double
  • long

The content you create upon the root folder can be stored on your local disk. The next time you start the application, you can load the same content again. On your local disk, it looks like this:

XML
<Registry>
  <Folder Key="Root">
    <Folder Key="Application">
      <Value Key="value1">
        <Content>1</Content>
        <Format>String</Format>
      </Value>
      <Value Key="value2">
        <Content>76</Content>
        <Format>String</Format>
      </Value>
      <Value Key="value3">
        <Content>6</Content>
        <Format>String</Format>
      </Value>
    </Folder>
  </Folder>
</Registry>

It starts with a root folder with the key "Root". The root folder has a folder with the key application. In this folder are values of type "string". These values are used to load / store the values from the example application. Let's take a close look at the design.

Using the Code

class view

The class diagram shows the design of the Registry component. The class Registry is the entry point for your application. The constructor of this class requires a RegistrySettings instance to create a new registry. This new registry supports access to the root folder, and offers the methods Save() and Load(). The Registry delegates the I/O operations to the Storage class. The root folder is an IEntry interface. The classes FolderEntry and ValueEntry implements the interface, and are the concrete implementations of folders and values. IEntry, FolderEntry and ValueEntry are designed based on the Composite Design Pattern. If something goes wrong, a RegistryException is thrown. the class RegistryUtils offers some static helper methods.

The composite design pattern allows you to create your own tree of objects. If you're not familiar with this pattern, then run Google, or buy a copy of the famous Gang of Four pattern book. It is not difficult. The IEntry interface supports the following methods and properties:

C#
public interface IEntry
{
    string Key { get; }
    bool IsFolder { get; }
    List<IEntry> Children { get; }
    IEntry this[string key] { get; }
    IEntry AddFolder(string key);
    IEntry AddValue(string key);

    void Remove(string key);
    bool Contains(string key);

    void SetValue(object value, ValueFormat format);
    object GetValue();
    ValueFormat GetValueFormat();
}

The interface provides a getter to determine the IEntry type. This getter returns the key and the children of the folder/value. The getter IsFolder returns a boolean, and lets you determine whether it is a folder or a value. The interface also supports an indexer. The methods in the interface can be used to add/remove/get content from the current folder/value. The usage of this interface is shown below in the example.

Example Code

First, we need to create a new registry instance.

C#
//
//  Create a new registry instance for the application.
//
const string FILE = "data.xml";

var settings = new RegistrySettings {StorageFile = FILE};
m_Registry = new Registry.Registry(settings);

This code creates an new registry, and stores the file to "data.xml" in the current directory. In your start up code, you can use something like that:

C#
var isAvailable = File.Exists(FILE);
if (isAvailable)
{
    m_Registry.Load();
}
else
{
    //
    //  Create the example structure
    //
    var root = m_Registry.Root;
    var app = root.AddFolder("Application");

    var value1 = app.AddValue("value1");
    var value2 = app.AddValue("value2");
    var value3 = app.AddValue("value3");

    value1.SetValue("1", ValueFormat.String);
    value2.SetValue("2", ValueFormat.String);
    value3.SetValue("3", ValueFormat.String);

    m_Registry.Save();
}

If there is content, we call the Load() method. Otherwise, there is no content, and we need to create it. First, we create a new folder, Application. The Application folder should contain three values. Each value gets a content in string format. Finally, we write the registry to disk.

C#
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
    //
    //  Get the values step by step.
    //
    var appFolder = m_Registry.Root["Application"];
    var value1Node = appFolder["value1"];
    var value2Node = appFolder["value2"];
    var value3Node = appFolder["value3"];

    value1Node.SetValue(textBox1.Text,ValueFormat.String);
    value2Node.SetValue(textBox2.Text, ValueFormat.String);
    value3Node.SetValue(textBox3.Text, ValueFormat.String);

    m_Registry.Save();
}

private void UpdateView()
{
    //
    //  Get the values in a much easier way.
    //
    var root = m_Registry.Root;
    var value1Node = RegistryUtils.GetNode("Application/value1",root);
    var value2Node = RegistryUtils.GetNode("Application/value2",root);
    var value3Node = RegistryUtils.GetNode("Application/value3",root);

    textBox1.Text = RegistryUtils.GetStringValue(value1Node);
    textBox2.Text = RegistryUtils.GetStringValue(value2Node);
    textBox3.Text = RegistryUtils.GetStringValue(value3Node);
}

This code shows how to use the registry. The entry point is the root folder. In the buttonSave_Click method, we write the latest changes to the registry and store them. In the UpdateView() method, we read the data from the registry. You see that there are some static methods for you convenience. RegistryUtils.GetNode(...) supports access to values/folders in a deeper level of the tree. RegistryUtils.GetStringValue() supports a type safe call to the value. You can also use the methods defined in the IEntry interface, but then you need to cast on your own.

That's it. This code can be found in the Zip file with the sources.

Points of Interest

The registry supports double values. How do you interpret the double value 3,200.5?

There are two possible answers (I know). The first one means 3 thousand and 200 point 5. The other means 3 point 2 with a useless point. The first one is used in the USA and a lot of other countries, and the second in Germany. The .NET Framework supports different cultures and number formats. If you serialize a double value with the German Culture settings, you get a different string than with US Culture settings, for example. This component uses the USA Culture for serializing double values in all cases, and solves this question above clearly.

This registry component was tested with NUnit. All tests were passed, and I don't know about any bugs at the moment. If you found one, then feel free to contact me. I hope you find it useful and it helps you a little bit.

History

  • 20.01.2009: V1.0.

License

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