Creating Your Own Dynamic Properties and Preserve Property Settings in Visual Basic .NET
Visual Studio Team
Summary: This paper demonstrates how to create dynamic properties in addition to the ones exposed by Microsoft® Visual Basic® .NET, as well as how to use dynamic properties to persist user-configurable properties between instances of an application. (6 printed pages)
Dynamic properties in Visual Basic .NET provide an easy way to preserve property settings between instances of an application, without having to resort to registry settings or .ini files. Visual Basic .NET provides a number of dynamic properties by default, and you can easily make other properties dynamic by adding code.
For example, by default Windows® Forms expose a number of dynamic properties such as MaximizeBox, MinimizeBox, and ShowInTaskbar, but do not expose other useful properties such as Size or Location.
Dynamic properties are stored in a configuration file; the configuration file must be edited in order to preserve a setting. It is not reasonable to expect end users to modify the configuration file each time they run the application. So it may appear that there is no way to preserve user-configurable properties, such as the last location of a form or a color preference.
By adding a small amount of code to your application, you can make any property act like a dynamic property, and make it user-configurable as well. In this article, you will learn how to set dynamic properties, how to make dynamic properties user-configurable, and how to create new dynamic properties.
Dynamic properties work by storing property values in the application's configuration file — app.config for Windows applications, web.config for Web applications. When a dynamic property is added in the Properties window, a key/value pair is added to the
appSettings section of the configuration file. In addition, a call to configurationAppSettings.GetValue is added to the InitializeComponent procedure; the return value is used to set the initial value of the property. When you edit the value in the configuration file, you alter the initial value of the property without having to modify the code in the InitializeComponent procedure.
For example, the TopMost property of a Windows Form determines whether a form always appears on top of any other windows. Normally you would set this property at design time, but it might be beneficial to make your application more flexible. By making the TopMost property a dynamic property, you can defer the choice until after the application is deployed.
To learn how dynamic properties work
- Create a new Windows application named DynamicProps and select the form designer.
- Find the (DynamicProperties) node in the Properties window and expand it, then click the ellipsis button next to the (Advanced) property to open the Dynamic Properties dialog box.
- Find the TopMost property in the Properties list and select it.
Note that the Key mapping is set to Form1.TopMost by default; you could enter any name here to be used as a key, but in this case you will accept the default value.
- Select the app.config file in Solution Explorer and open it.
You should see the line
<add key="Form1.TopMost" value="false" />in the
<appSettings>section. The key matches the default key mapping that you set in the dialog box, and the value is the default value (false) for the TopMost property.
- Open the Code Editor for Form1 and expand the region labeled Windows Form Designer generated code. In the InitializeComponent procedure, you should find a declaration:
Dim configurationAppSettings As _ System.Configuration.AppSettingsReader = New _ System.Configuration.AppSettingsReader()
This declaration creates an
AppSettingsReaderclass that will parse the configuration file.
Further down in the procedure you should find the following line:
Me.TopMost = CType(configurationAppSettings.GetValue("Form1.TopMost", _ GetType(System.Boolean)), Boolean)
This line retrieves the value for the key Form1.TopMost from the configuration file and converts it from the Text data type used to store it in the configuration file to the Boolean type required for the TopMost property.
- Run the application and note that the form does not stay on top, then close the application.
- Open the app.config file and change the value for the Form1.TopMost key to true.
- Run the application again.
This time the form does stay on top.
If you look at the TopMost property in the Properties window, you will notice that the property has been changed to true. This happens because there is a dynamic linkage between the property and the configuration file.
In the next section, you will learn how to make the TopMost property user-configurable at run time.
Although it is useful to change a dynamic property by editing the configuration file, it would be even more useful if the property were user-configurable so that the user could change the dynamic property from the application. Furthermore, the application should save and restore this setting between instances. In order to do this, you must first add a user-interface element to set the property.
To persist a user-configurable dynamic property
- Add a CheckBox control to the form, and set its Text property to Stay on top.
- Add the following code to the CheckBox1_CheckedChanged event handler:
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged Me.TopMost = CheckBox1.Checked End Sub
- Change the Checked property of the check box to true so that the initial state of the check box matches the initial setting of the TopMost property (which you changed to true in the previous section).
- Run the application.
When the check box is selected, the form stays on top; when it is cleared the form does not stay on top.
- Make sure that the check box is still cleared and close the application.
- Open the app.config file and note that the value for the TopMost property is still true.
What is happening? Although the application can read the configuration file during initialization, there is no way to automatically write the values to the configuration file when they are changed. You will need to add your own code for that.
You could add the code to the CheckedChanged event handler so that every time the property is changed, the configuration file is updated. It is more efficient, however, to simply update the configuration file each time the application is closed.
- Add the following code to the Form1_Closing event handler:
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As _ System.ComponentModel.CancelEventArgs) Handles MyBase.Closing ' Use reflection to find the location of the config file. Dim Asm As System.Reflection.Assembly = _ System.Reflection.Assembly.GetExecutingAssembly Dim strConfigLoc As String strConfigLoc = Asm.Location ' The config file is located in the application's bin directory, so ' you need to remove the file name. Dim strTemp As String strTemp = strConfigLoc strTemp = System.IO.Path.GetDirectoryName(strConfigLoc) ' Declare a FileInfo object for the config file. Dim FileInfo As System.IO.FileInfo = New _ System.IO.FileInfo(strTemp & "\DynamicProps.exe.config") ' Load the config file into the XML DOM. Dim XmlDocument As New System.Xml.XmlDocument() XmlDocument.Load(FileInfo.FullName) ' Find the right node and change it to the new value. Dim Node As System.Xml.XmlNode For Each Node In XmlDocument.Item("configuration").Item("appSettings") ' Skip any comments. If Node.Name = "add" Then If Node.Attributes.GetNamedItem("key").Value = _ "Form1.TopMost" Then Node.Attributes.GetNamedItem("value").Value _ = CType(Me.TopMost, String) End If End If Next Node ' Save the modified config file. XmlDocument.Save(FileInfo.FullName) End Sub
- Run the application again, clear the check box, and then close the application.
- Open the app.config file; the value for Form1.TopMost should now be false.
If you run the application again, the form should no longer stay on top.
Why is the check box is still selected? If you test the behavior of the form, you will find that it does not stay on top, so the property setting did take hold. The problem is that the Checked property of the check box is now out of sync.
- Add the following code to the Sub_New procedure, immediately after the call to InitializeCompoment, to get the Checked property back in sync:
CheckBox1.Checked = Me.TopMost
Now when you run the application, the state of the check box should accurately reflect the TopMost behavior. But you still have another problem: If you select the check box, then close and restart the application, the check box is no longer selected. Why? There are actually two configuration files – the app.config file that you see in Solution Explorer, and a appname.exe.config file in the bin directory. If you look carefully at the code, you will notice that you are actually saving the value to the DynamicProps.exe.config file. You can see this file by clicking Show All Files in the Solution Explorer toolbar.
Each time the project is built, the contents of the app.config file are copied to the DynamicProps.exe.config file, overwriting your changes. When the application is deployed, the DynamicProps.exe.config file is deployed along with the application into the application directory. You could write directly to the app.config file, but because it resides on a level below the application, your code would not be able to find it after deployment. In order to apply the changes at design time, you will need to open the DynamicProps.exe.config and copy the contents to the app.config file before running the application again.
What if you want to save a property that is not exposed as a dynamic property? In the next section, you will make additional properties dynamic.
In addition to the properties that are exposed as dynamic properties by default, you might also want to preserve other properties. For example, a common scenario would be to save the location of a form so that if a user has moved it, it will reopen at that location. Although the Location property of a form is not exposed as a dynamic property, you can make it act as a dynamic property by editing the app.config file.
The Location property actually consists of two properties, Location.X and Location.Y, so you will need to deal with each of them separately.
To create a new dynamic property
- Add two new lines to the app.config file immediately below the line for the TopMost property:
<add key="Form1.X" value="0" /> <add key="Form1.Y" value="0" />
- Close the app.config file in order to save it; otherwise, you will be prompted each time the application is run.
- Add the following code to the bottom of the procedure, which will modify the Sub_New event handler to read the values from the configuration file.
Dim configReader As System.Configuration.AppSettingsReader = New _ System.Configuration.AppSettingsReader() Dim x As Integer, y As Integer ' Retrieve the value from the config file and convert to Integer. x = CType(configReader.GetValue("Form1.X", _ GetType(System.Int32)), Integer) y = CType(configReader.GetValue("Form1.Y", _ GetType(System.Int32)), Integer) ' Set the location using the new x and y coordinates. Me.Location = New System.Drawing.Point(x, y)
Note that you created a new AppSettingsReader object, because the one used in the InitializeComponent procedure is not accessible. The X and Y properties have separate keys in the configuration file, so you set each individually. Finally, the Location property takes a Point object as an argument, so you create a point specifying the X and Y coordinates.
- Modify the Form1_Closing event handler to save the Location.X and Location.Y values to the configuration file. Add the following code after the line
If Node.Name = "add" Then:
If Node.Attributes.GetNamedItem("key").Value = "Form1.X" Then ' Convert the value of X to a string. Node.Attributes.GetNamedItem("value").Value = _ CType(Me.Location.X, String) End If If Node.Attributes.GetNamedItem("key").Value = "Form1.Y" Then Node.Attributes.GetNamedItem("value").Value = CType(Me.Location.Y, _ String) End If
- Run the application, move the form, then close and restart the application.
The form should be restored to its last location, but it is not. Why? During initialization, the Location property is overridden by the StartPosition property; the default value for StartPosition is WindowsDefault, which means Windows will determine the initial position of the form.
- Add another line to the Sub_New procedure so that your property setting will take hold:
Me.StartPosition = FormStartPosition.Manual
This time when you run and restart the application, the form will be restored to its previous location as expected.
As you can see, creating your own dynamic properties is not difficult, requiring just a few lines of code in your initialization and termination procedures. Although this article focuses on Windows applications, the same techniques can just as easily be used for Web applications. Given a choice between an application that saves user settings and one that does not, most users will choose the former.