Windows Forms - Creating and Persisting Custom User Settings in C#






4.97/5 (18 votes)
This article discusses the creation and persistence of .NET Framework and custom objects within the Windows User Settings.
Introduction
If you have configured property settings, you no doubt noticed the “Browse” choice as illustrated in Figure 1 to configure a limited set of objects, and I do mean “limited”. After much pain due to countless hours of webpage browsing that, in most cases, leads to dead ends, one can eventually figure out how to correctly use the “Browse” choice to persist the various objects that are provided in the “Browse” choice list.
While the bad news is that we are not going to discuss how to use the “Browse” choice list, the good news is that we will discuss how to persist custom objects (instances of classes you create) and just about any object the Framework provides. What this means is that the painful experience of trying to use the “Browse” choice to persist objects never has to be used until Microsoft does a better job of making it developer friendly.
OK, so maybe, you don’t agree, and find the “Browse” choice easy to use. If that is the case, you may still find this article interesting. Among other things, we will persist a “System.Collections.Generic.SortedList
” object which is not found in the “Browse” choice list. Additionally, we will persist a custom (user defined) object.
Using the Code
The first code snippet we will discuss is a class shown below. The class is used to persist a Generic SortedList
. The GenericSortedListDemo
class, like any class object we want to persist, must inherit ApplicationSettingsBase
.
The next important thing to notice are the attributes on the top of the NamesGenericSortedList
property.
The UserScopedSetting
attribute, as opposed to the ApplicationScopedSetting
attribute, is very important. It is that attribute that will allow you to persist the object. If you did use the ApplicationScopedSetting
attribute, what would happen is that when you call the Save
method (as seen in the code you can download) to persist the object, the Save
method will lead you to believe the object was written to disk when, in fact, it was not!
The SettingsSerializeAs
attribute must specify Binary
as our code does.
The last attribute, DefaultSettingValue
, has the effect of creating an object with no members, meaning the count is zero for the Generic SortedList
object that is created.
Our job then is to create an instance of GenericSortedListDemo
, fill it with data, then persist it to disk. You can see how that is done by viewing the code you download. Exactly where it is stored will not be discussed here.
internal sealed class GenericSortedListDemo : ApplicationSettingsBase
{
// If you used [ApplicationScopedSetting()] instead of [UserScopedSetting()],
// you would NOT be able to persist any data changes!
[UserScopedSetting()]
[SettingsSerializeAs(System.Configuration.SettingsSerializeAs.Binary)]
[DefaultSettingValue("")]
public System.Collections.Generic.SortedList<int, /> NamesGenericSortedList
{
get
{
return ((System.Collections.Generic.SortedList<int, />)
this["NamesGenericSortedList"]);
}
set
{
this["NamesGenericSortedList"] =
(System.Collections.Generic.SortedList<int, />)value;
}
}
}
The next object that we will discuss is a custom (user defined) object. You will find that creating an instance of a custom object, filling it, and then persisting it to disk is very similar to what we just did with a System.Collections.Generic.SortedList
object.
Our custom class is called EmployeeList
. In that class shown below, you will see a required attribute: [Serializable]
. Without it, you will not be able to persist an instance of the class. The class contains an ArrayList
which consists of System.Collections.DictionaryEntry
objects, where each DictionaryEntry
object contains an employee ID and employee name.
[Serializable]
internal class EmployeeList
{
private ArrayList _employees = null;
public EmployeeList()
{
_employees = new ArrayList();
}
public System.Collections.IEnumerator Employees
{
get
{
return _employees.GetEnumerator();
}
}
public void AddEmployee(int id, string name)
{
_employees.Add(new DictionaryEntry(id, name));
}
public void RemoveEmployee(int id, string name)
{
DictionaryEntry de = new DictionaryEntry(id, name);
if (_employees.Contains(de))
{
_employees.Remove(de);
}
}
public void ClearAllEmployees()
{
_employees.Clear();
}
public int EmployeesCount
{
get
{
return _employees.Count;
}
}
}
Below is the code that utilizes an instance of EmployeeList
. It is very similar to what we did earlier.
internal sealed class CustomEmployeesDemo : ApplicationSettingsBase
{
// If you used [ApplicationScopedSetting()] instead of [UserScopedSetting()],
// you would NOT be able to persist any data changes!
[UserScopedSetting()]
[SettingsSerializeAs(System.Configuration.SettingsSerializeAs.Binary)]
[DefaultSettingValue("")]
public EmployeeList CustomEmployees
{
get
{
return ((EmployeeList)this["CustomEmployees"]);
}
set
{
this["CustomEmployees"] = (EmployeeList)value;
}
}
}
Before you get overly critical of our custom class design, please keep in mind that it is merely an example of how to create and persist custom objects. In a real world scenario, a custom class would certainly not be designed like the demo custom class.