Introduction
In the Windows development history, the problem of maintaining the configuration settings for an application has been faced in many ways. For example, in Windows 3.1, the classic approach to the problem was in the use of INI files; more recently a standard way to store config settings was in reading/writing Windows Registry keys. Now, in the .NET age, we can take advantage of the power and simplicity of .config files that I consider a sort of return to the INI approach (simply due to the need of storing config settings near the application's assemblies, to make the XCOPY deployment possible).
Of course, their text-based, simply editable XML structure make .NET configuration files very handy. But, when you use the ConfigurationSettings
class (from the System.Configuration
namespace) to access them, there are two main limitations:
- the name for the application configuration file is one and fixed (i.e. web.config or app.exe.config) and
- the
AppSettings
property of the ConfigurationSettings
class allows only a read-only access to the values stored inside the file.
The simple VB.NET class I propose in this article shows a way to manage configuration settings in a read/write (not read-only) fashion, and to allow multiple configuration files for the same application. The proposed approach is - of course - custom, and it is not related to the classic .NET .config files and their structure, but:
- the idea of text-based, XML configuration files has been kept;
- configuration settings continue to be stored as key/value pairs;
- taking advantage of the ADO.NET
DataSet
serialization features, this class is implemented in a few lines of code.
The code
Imports System.IO
Public Class ConfigOpt
Private Shared DSoptions As DataSet
Private Shared mConfigFileName As String
Public Shared ReadOnly Property ConfigFileName() As String
Get
Return mConfigFileName
End Get
End Property
Public Shared Sub Initialize(ByVal ConfigFile As String)
mConfigFileName = ConfigFile
DSoptions = New DataSet("ConfigOpt")
If File.Exists(ConfigFile) Then
DSoptions.ReadXml(ConfigFile)
Else
Dim dt As New DataTable("ConfigValues")
dt.Columns.Add("OptionName", System.Type.GetType("System.String"))
dt.Columns.Add("OptionValue", System.Type.GetType("System.String"))
DSoptions.Tables.Add(dt)
End If
End Sub
Public Shared Sub Store()
Store(mConfigFileName)
End Sub
Public Shared Sub Store(ByVal ConfigFile As String)
mConfigFileName = ConfigFile
DSoptions.WriteXml(ConfigFile)
End Sub
Public Shared Function GetOption(ByVal OptionName As String) As String
Dim dv As DataView = DSoptions.Tables("ConfigValues").DefaultView
dv.RowFilter = "OptionName='" & OptionName & "'"
If dv.Count > 0 Then
Return CStr(dv.Item(0).Item("OptionValue"))
Else
Return ""
End If
End Function
Public Shared Sub SetOption(ByVal OptionName _
As String, ByVal OptionValue As String)
Dim dv As DataView = DSoptions.Tables("ConfigValues").DefaultView
dv.RowFilter = "OptionName='" & OptionName & "'"
If dv.Count > 0 Then
dv.Item(0).Item("OptionValue") = OptionValue
Else
Dim dr As DataRow = DSoptions.Tables("ConfigValues").NewRow()
dr("OptionName") = OptionName
dr("OptionValue") = OptionValue
DSoptions.Tables("ConfigValues").Rows.Add(dr)
End If
End Sub
End Class
How to use the code
All the methods the ConfigOpt
class exposes are static, so there is no need to create an instance of the class.
Call the Initialize
method to read and put in a memory cache an existing config file. You need to call this method also in absence of an existing config file, to initialize the data structure used by ConfigOpt
class.
Call the GetOption
method to read a configuration value, given its key. Keep in mind that read values are always kept from the cached config key/value pairs (not from the config file directly).
Call the SetOption
method to add or update a configuration setting, in the form of a key/value pair. Keys and values are always String
types. Keep in mind that addition and updation of config settings are done only in the cached memory structure; to persist all the config key/value pairs on the file system, you need to call the Store
method.
Typically, the Store
method is called once (when the user closes the application, or he closes a configuration dialog box, or he explicitly wants to save his settings), but you may call this method more often if you need to immediately persist on file the ConfigOpt
internal data changes.
An example
Suppose you have, in a Windows Forms application of yours, a configuration form containing some controls (textboxes, checkboxes, etc.); if you need to persist the state of these controls on a file so that their values are maintained between different user sessions, you can use the ConfigOpt
class in this way:
Private Sub Form_Load(...) Handles MyBase.Load
ConfigOpt.Initialize("MyConfig.cfg")
ReadConfigValues()
End Sub
Private Sub ReadConfigValues()
txtMyTextbox.Text = ConfigOpt.GetOption("txtMyTextbox")
chkMyCheckbox.Checked = Boolean.Parse(ConfigOpt.GetOption("chkMyCheckbox"))
...
End Sub
Private Sub Form_Closing(...) Handles MyBase.Closing
WriteConfigValues()
ConfigOpt.Store()
End Sub
Private Sub WriteconfigValues()
ConfigOpt.SetOption("txtMyTextbox", txtMyTextbox.Text)
ConfigOpt.SetOption("chkMyCheckbox", chkMyCheckbox.Checked.ToString())
....
End Sub
The generated configuration file will contain this text that you can edit with Notepad whenever you want:
="1.0" ="yes"
<ConfigOpt>
<ConfigValues>
<OptionName>txtMyTextbox</OptionName>
<OptionValue>Sample string typed in txtMyTextbox.</OptionValue>
</ConfigValues>
<ConfigValues>
<OptionName>chkMyCheckbox</OptionName>
<OptionValue>True</OptionValue>
</ConfigValues>
...
</ConfigOpt>
As suggested by some CodeProject readers (thank you!), you may prefer a more compact XML file with this structure:
="1.0" ="yes"
<ConfigOpt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ConfigValues OptionName="txtMyTextbox">
Sample string typed in txtMyTextbox.</ConfigValues>
<ConfigValues OptionName="chkMyCheckbox">True</ConfigValues>
</ConfigOpt>
In this case, you need to modify the Else
branch in the ConfigOpt.Initialize
method (where the DataTable
is defined), modifying the default XML column mapping as follows:
Dim dt As New DataTable("ConfigValues")
dt.Columns.Add("OptionName", System.Type.GetType("System.String"))
dt.Columns.Add("OptionValue", System.Type.GetType("System.String"))
dt.Columns("OptionName").ColumnMapping = MappingType.Attribute
dt.Columns("OptionValue").ColumnMapping = MappingType.SimpleContent
DSoptions.Tables.Add(dt)