Save your UserSettings in an Isolated Store
By Fons Sonnemans (February 2003)
Download
UserSettings.zip
Introduction
I like applications who remember my settings the next time I use it. This is an
easy feature which is often forgotten. Most of the time because it is quite some
work. With this article I want to help you with this and saving you a lot off work.
Then there is always the question 'where do we store the settings?'. I see a lot
of applications using the Registry, ini-files or xml-files. All these solutions
are causing a security risk. Especially when you want your application to be downloaded
from the web using the 'no touch' deployment features of .NET.
Microsoft has solved this problem for me by introducing an isolated stores. With
these stores, you can read and write data that less trusted code cannot access and
prevent the exposure of sensitive information that can be saved elsewhere on the
file system. Data is stored in compartments that are isolated by the current user
and by the assembly in which the code exists.
The downloadable zipfile contains a UserSettingLibrary which can be used to store
user settings into an isolated store.
IsolatedUserSettings class
The IsolatedUserSettings class can be used to store and retrieve user settings in
and from an IsolatedStorageFile. The RegisterForm() method can be used to register
a form. The location, size and windowstate of the form will be saved and restored.
The SaveSetting() method saves an serializable object which can be restored
using the GetSetting() method.
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
namespace ReflectionIT.UserSettings
{
public
class IsolatedUserSettings : IDisposable
{
private
Hashtable _settings;
private
bool _closed
= false;
private
string _fileName;
public
IsolatedUserSettings(string
fileName)
{
_fileName
= fileName;
IsolatedStorageFile
isoFile = GetStore();
string[]
files = isoFile.GetFileNames(fileName);
if
(files.Length
==
1) {
using (IsolatedStorageFileStream
stream =
new IsolatedStorageFileStream(
fileName, FileMode.Open,
isoFile))
{
BinaryFormatter
formatter = new BinaryFormatter();
try {
_settings =
(Hashtable)formatter.Deserialize(stream);
}
catch (SerializationException) {
_settings =
new Hashtable();
}
}
}
else {
_settings =
new Hashtable();
}
}
~IsolatedUserSettings()
{
Dispose(false);
}
public
void Dispose() {
Dispose(true);
System.GC.SuppressFinalize(this);
}
protected
virtual void Dispose(bool
disposing) {
this.Close();
}
public
virtual void Close()
{
if
(!_closed) {
IsolatedStorageFile
isoFile = GetStore();
using (IsolatedStorageFileStream
stream =
new IsolatedStorageFileStream(
FileName, FileMode.Create, isoFile)) {
BinaryFormatter
formatter = new BinaryFormatter();
formatter.Serialize(stream,
_settings);
}
_closed =
true;
}
}
public
string FileName {
get
{ return
_fileName; }
}
private
IsolatedStorageFile GetStore()
{
return
IsolatedStorageFile.GetStore(IsolatedStorageScope.Assembly |
IsolatedStorageScope.User | IsolatedStorageScope.Domain |
IsolatedStorageScope.Roaming,
null, null);
}
public
virtual void RegisterForm(string
key, Form form) {
FormSetting
f;
if
(_settings.ContainsKey(key))
{
f
= (FormSetting)_settings[key];
f.Restore(form);
}
else {
f
= new FormSetting(key);
f.Backup(form);
_settings.Add(key, f);
}
form.SizeChanged +=
new EventHandler(f.FormMovedOrResized);
form.LocationChanged += new EventHandler(f.FormMovedOrResized);
}
public
virtual void UnRegisterForm(string
key, Form form) {
if
(_settings.ContainsKey(key))
{
FormSetting
f = (FormSetting)_settings[key];
f.Backup(form);
form.SizeChanged
-=
new EventHandler(f.FormMovedOrResized);
form.LocationChanged
-=
new EventHandler(f.FormMovedOrResized);
}
}
public
void SaveSetting(string key,
object setting) {
TypeAttributes
attributes = setting.GetType().Attributes;
if
((attributes
& TypeAttributes.Serializable) ==
0)
{
throw new ArgumentException("Setting argument must be marked Serializable.");
}
if (_settings.ContainsKey(key))
{
_settings[key] =
setting;
}
else {
_settings.Add(key,
setting);
}
}
public
object GetSetting(string key)
{
return
GetSetting(key, null);
}
public
object GetSetting(string key,
object defaultValue) {
if
(_settings.ContainsKey(key))
{
return _settings[key];
}
return
defaultValue;
}
}
}
The FormSetting class is used to store a form's location, size and windowstate in
a serializable object. The next time you register a form with the same key the location,
size and windowstate is restored to the saved values. So if you resize your form,
close it and open it again the size will be the same as it was when you last closed
it.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ReflectionIT.UserSettings
{
[Serializable]
public class
FormSetting
{
private
Point _location;
private
string _name;
private
Size _size;
private
FormWindowState _windowState;
public
FormSetting(string
name)
{
this._name
= name;
}
public
string Name {
get
{ return
(this._name);
}
}
public
virtual void Backup(Form form)
{
if
(form.WindowState
!= FormWindowState.Minimized)
{
if (form.WindowState !=
FormWindowState.Maximized)
{
this._location
= form.Location;
this._size
= form.Size;
}
this._windowState
= form.WindowState;
}
}
public
virtual void FormMovedOrResized(object
sender, EventArgs e)
{
Backup((Form)sender);
}
public
virtual void Restore(Form form)
{
form.Location =
this._location;
form.Size =
this._size;
form.WindowState = this._windowState;
}
}
}
Sample
The included sample uses the IsolatedUserSettings class to store and restore the
user settings. In this case the Form (Location, Size and WindowState), the Checked
property of a CheckBox and a MostRecentlyUsedList object. The settings are restored
in the constructor and saved in the Closing event handler, the Form properties are
saved automatically by the internally used FormSetting class. You must set
the StartPosition property of the form to 'Manual', otherwise the location
will not be restored.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using ReflectionIT.UserSettings;
namespace Test
{
public
class Form1 :
System.Windows.Forms.Form
{
private
System.Windows.Forms.GroupBox groupBox1;
private
System.Windows.Forms.Button buttonMRUAdd;
private
System.Windows.Forms.TextBox textBoxMRU;
private
System.Windows.Forms.ListBox listBoxMRU;
private
MostRecentlyUsedList _mru;
private
IsolatedUserSettings _settings
= new IsolatedUserSettings("settings.dat");
private
System.Windows.Forms.CheckBox checkBox1;
private
System.ComponentModel.Container
components = null;
public
Form1()
{
InitializeComponent();
_settings.RegisterForm(this.Name, this);
_mru=(MostRecentlyUsedList)_settings.GetSetting("mru",new MostRecentlyUsedList(5));
checkBox1.Checked =
(bool)_settings.GetSetting("checked",
true);
listBoxMRU.DataSource =
_mru;
}
protected
override void Dispose( bool
disposing )
{
if( disposing
)
{
if (components
!=
null)
{
components.Dispose();
}
}
base.Dispose(
disposing );
}
[STAThread]
static
void Main()
{
Application.Run(new
Form1());
}
private
void buttonMRUAdd_Click(object
sender, System.EventArgs
e) {
_mru.Add(textBoxMRU.Text);
textBoxMRU.Clear();
textBoxMRU.Focus();
listBoxMRU.DataSource =
null; listBoxMRU.DataSource
= _mru;
}
private
void Form1_Closing(object
sender, System.ComponentModel.CancelEventArgs e)
{
_settings.SaveSetting("mru", _mru);
_settings.SaveSetting("checked", checkBox1.Checked);
_settings.Close();
}
}
}
The MostRecentlyUsedList class has a Serializable attribute which makes it possible
to store it in an IsolatedUserSetting.
using System;
using System.Collections;
namespace Test
{
       [Serializable]
 public class
MostRecentlyUsedList : IList
 {
   private
int _capacity;
   private
ArrayList _list;
                           public
MostRecentlyUsedList(int
capacity)
   {
     _capacity
= capacity;
     _list
= new ArrayList(capacity);
   }
                               public
void Add(object
value)
   {
     if
(_list.Contains(value)) {
       _list.Remove(value);
     }
else {
       if (this.Count ==
this.Capacity) {
         _list.RemoveAt(this.Count
- 1);
       }
     }
     _list.Insert(0, value);
   }
                   public
int Capacity {
     get
{ return
(this._capacity);  }
     set
{
       this._capacity
= value;
       while (this.Count >=
this.Capacity) {
         _list.RemoveAt(this.Count
- 1);
       }
     }
   }
   ....
Conclusion
The IsolatedUserSettings is an simple class which uses an isolated store to serialize
and deserialize objects. With it you can enhance your applications without much
effort.
Any suggestions and feedback for improving this article is most welcome. Send your
suggestions and feedback to Fons.Sonnemans@reflectionit.nl