Click here to Skip to main content
Click here to Skip to main content

Properties, Controls and XML: AutoSettings

, 16 Jun 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
How to connect a control, a property and an entry in an XML-file
Screenshot from test application

Introduction

Allowing the user of your oh-so-fancy GUI to customize the experience by the use of settings is a very nice feature for any program. However, the user expects all settings to take effect immediately, which means that you have to listen to multiple events (or, alternatively, use data binding). The user also expects all settings to be there the next time he or she opens the program, which means that you also have to store settings in the registry or to an XML-file.

If you feel like I do - that the solutions using data binding and settings are awkward to use - then you might already have found the excellent code by circumpunct. This is a rather large extension of circumpunct's work, in that it allows you to connect a control, a property and an entry in an XML-file. Any change to the value of a control or of a property is immediately mirrored in the bound entities.

(If you are familiar with WPF, you can view AutoSettings as a way of binding the DataContext and BindingPath of a control to a property in .NET 2.0.)

Using the Code

Using the code is very straight-forward. There are several public methods, but the only one that you need to worry about is ConnectSetting:

public bool ConnectSetting
    (Control control, string xPath, object propertyContainer, string propertyName)

You specify the control to bind to, the XML-path to store the value under, the object that contains the property, and the name of the property that should hold the value:

// Create a new settings class, using the default filename settings.xml
mySettings = new AutoSettings.AutoSettings();

// Create a new property container
myProperties = new PropertyContainer();

// Connect controls to settings file and property class
// Note that property names are case sensitive!

// Connect a check box to the corresponding property. 
mySettings.ConnectSetting(cbBlack, "isBlack", myProperties, "IsBlack");
// Text from textboxes works the same way:
mySettings.ConnectSetting(tbText, "text", myProperties, "Text");
// Comboboxes are slightly more complicated (examine the settings file!):
mySettings.ConnectSetting(cbOptions, "optionText", myProperties, "OptionText");

// Numeric up-downs are also supported: 
mySettings.ConnectSetting(nudA, "numberA", myProperties, "NumberA");

ConnectSetting checks if the XML-path exists in the settings file. If it does, that value will be read and written to the control and the property. If the path does not exist, it will be created. It is important to realize that control, xPath and the propertyContainer/propertyName combo can all be null. Thus, if you do not need to reflect the values of a control into a property, set propertyContainer and propertyName to null:

// In some cases, you are not interested in mirroring the control into a separate
// property (perhaps because you can retrieve the value directly anyway):
mySettings.ConnectSetting(rbRed, "isRed", null, null);

If you do not have a control, but still wish to bind a property to a setting, set control to null:

// It is also possible to ignore the control input, and just store the property
// in the settings file (useful if you want to design your own control):
mySettings.ConnectSetting(null, "numberB", myProperties, "NumberB");

Finally, if you do not want to store the setting to file, but nevertheless wish to bind a control to a property, set xPath to null:

// You can also explicitly make sure that the value is not saved to a file, although
// you want it mirrored into a property. Useful for passwords, or similar.
mySettings.ConnectSetting(rbYellow, null, myProperties, "IsYellow");

The choice is yours.

ConnectSetting checks if the propertyContainer implements INotifyPropertyChanged. If it does, it listens to this event and updates the control and settings file whenever the property changes. This allows you to programmatically change a value of a property, without having to worry about explicitly updating the control and settings file. Thus, if PropertyContainer looks like this:

public partial class PropertyContainer : INotifyPropertyChanged
{
    private decimal myNumberA;

    public decimal NumberA
    {
        get { return myNumberA; }
        set 
        { 
            myNumberA = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("NumberA"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

You can simply do:

myProperties.NumberA++;

This will update the property NumberA, the control bound to this property (nudA) and the contents of the settings file ("numberA").

How It Works

While easy to use, there is a lot going on behind the scenes. For example, how is the XML-string converted into a proper value? For every value stored in the settings file, there is also a type. This allows the XML-reader to find a proper converter and convert the value into a type, like this:

private T ConvertFromString<t>(string value) where T : IConvertible
{
    if (string.IsNullOrEmpty(value)) return default(T);
    return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value);
}

As has already been hinted at, the properties are read and updated by using Reflection. For example:

PropertyInfo propertyInfo = propertyContainer.GetType().GetProperty(propertyName);
if (propertyInfo != null)
{
    propertyInfo.SetValue(propertyContainer, GetSetting(xPath), null);
}

For further details, see the code or ask a question in the message area below.

Limitations

The code is light-weight, and does little or no error checking for corrupt settings files.

The code supports five types of controls: CheckBox, RadioButton, TextBox, NumericUpDown and ComboBox. It should be fairly clear how to do the extensions to other types of controls. Let me know if you have a specific control that you want to see support for.

The "binding" is used only for the default use of the control, i.e. for a CheckBox, the value stored is the Checked property. There might be other properties of the controls that might be of interest to store as well, but this is not implemented. It should also be noted that AutoSettings does not use Invoke to update the control, which means that you (in most cases) cannot update a property from a thread other than the GUI thread. This will be fixed in a future version.

History

  • 2007-06-16: Initial version

License

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

Share

About the Author

Chris_42
Web Developer
Sweden Sweden
Christoffer is a PhD-student in Autonomous Robotics at AASS, Örebro University, Sweden.
 

Comments and Discussions

 
QuestionControl array Pinmemberpsymon256-Oct-07 16:30 
AnswerRe: Control array PinmemberChris_427-Oct-07 1:47 
GeneralRe: Control array Pinmemberpsymon257-Oct-07 7:24 
Questiondoes this rely on WPF ? PinmemberBillWoodruff17-Jun-07 15:34 
AnswerRe: does this rely on WPF ? PinmemberChris_4217-Jun-07 22:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 16 Jun 2007
Article Copyright 2007 by Chris_42
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid