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:
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:
<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
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:
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 IsFo
lder 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.
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:
var isAvailable = File.Exists(FILE);
if (isAvailable)
{
m_Registry.Load();
}
else
{
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.
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
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()
{
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