Click here to Skip to main content
12,698,410 members (22,626 online)
Click here to Skip to main content
Add your own
alternative version


69 bookmarked

Saving the state (serializing) a Windows Form

, 13 Jun 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Describes how to save the state of all controls on a form, and restore them at a later time


It's a fairly common requirement to want to save the data when a user closes a form. In many applications nowadays, this is done with a database. Reopening the form involves reading the data back from the database, and populating all the form's controls with the data. Apart from the amount of time it can take to code all of this, maintaining it can be a nightmare, especially if there are a lot of controls on the form. Every time you change the form, you have to change the code to save the data.

I was recently working on an application where a database wasn't really practical, but I wanted to save a form's data for next time. The obvious way to do this would be to serialise the form object to disk. The .NET Framework allows you to mark classes with [Serializable], and then the framework does (almost) all of the hard work for you.

Unfortunately, this doesn't work with Windows Forms, as they are not marked as [Serializable], so the compiler throws an error if you try this with your own form.

Surely Someone Must Have Done This Already?

After spending fruitless hours searching, I came to the conclusion that I wasn't going to find a ready-made solution to this problem, and so I decided to write my own form serialiser. I wanted to make it reusable enough that it could be dropped into any project, and a form saved with the minimum of effort.

I was pleased enough with the results to want to post it here in case it's of use to anyone else. The test project that comes in the download has a main form that has just one button:


Clicking this button opens a child form, that has some (rather stupid) controls that ask you about your state of mind!


If you fill in some values and then click the "OK" button, the form is closed. If the "Serialise?" checkbox was checked, then the data you entered is saved in an XML file. If you now click the "Open" button on the main form, the child form is opened, and repopulated with the same data. If you change the data, uncheck the "Serialise?" checkbox and then click "OK" again, the data will not be serialised, so when you click the "Open" button on the main form, the child form will open again, but with the data from the previous instance will not be shown. If you delete the XML file, then when the child form opens, the controls will be empty.

In line with my desire to make this as simple as possible, saving the form data is done with one call to the serialisation method, and restoring it is done with one more. It doesn't matter how complex your form is, the one call is all you need.

Using the Code

To use this code in your own project, all you need to do is drop the FormSerialisor.cs file into your project, and then add the following line at the top of any class where you wish to use the code:

using FormSerialisation;

The FormSerialisor class is static, so you don't need to create an instance of it. You just call the static methods and pass in the form (or control, see later) you want serialising, and the full path to the XML file you want to use for saving the data.

Serialising and deserialising are now really easy. You can either do this from within the form itself, or from the code that calls the form. For example, if you wanted a form to serialise itself, you just use the following line:

FormSerialisor.Serialise(this, Application.StartupPath + @"\serialise.xml");

This will save the form's data in a file called serialise.xml in the same folder as where the executable lives.

If you then want to restore the data when the form next loads, then you would add the following code to the Form_Load event handler:

FormSerialisor.Deserialise(this, Application.StartupPath + @"\serialise.xml");

This would look for the XML file, and if it exists, populate the form's controls with data from the file. If the file doesn't exist, nothing will happen.

So How Does It Work?

The code for this is fairly straightforward, but there are a few small issues that I had to consider. The serialisation is done by enumerating all the controls on the form, and saving the data for each one in turn.

The Serialise(Control c, string XmlFileName) method creates an XmlTextWriter object that handles writing out the XML file, and then calls the recursive method, AddChildControls, passing the form as a parameter. Notice that the signature for this method specifies a Control as the first parameter. Since a Form is really a type of Control, this works fine if you pass in a form, but means that you don't have to serialise the whole form if you don't want. In the application where I first used this, I passed a TabControl in, as I only wanted to serialise the controls on that, and not the toolbar controls, etc., that were also on the main form.

If a control has child controls (if it were a Panel or GroupBox for example), then the child controls are enumerated and saved. This is all done by calling AddChildControls recursively, and passing the current container control as a parameter.

I decided not to serialise Label controls, as these are not user-changeable, and are not commonly changed in code (in my experience anyway). There's no harm in serialising Label controls, it just makes the XML file a bit bigger. If you change the text of your Label controls in code, you may want to remove the following line (and the corresponding closing bracket) from the AddChildControls method:

if (!(childCtrl is Label)) {

You can prevent other types of controls from being serialised as well by changing this line. For example, if you don't want the state of Button controls to be serialised (which is another fairly common case), you can just change the line to:

if (!(childCtrl is Label) && !(childCtrl is Button)) {

In truth, as disk space is so cheap nowadays, there isn't really any necessity to do this, but I left it in anyway. I used this code on a form with about 250 controls, and even including Label controls, the XML file only grew from about 77KB to about 92KB, so the extra file size is probably not worth worrying about.

Handling Different Types of Controls

All controls have the Visible property saved as this is a property that is common to all of them, and it's one I change often in code. The AddChildControls method handles different types of controls individually, so that control-specific properties can be serialised:

if (childCtrl is TextBox) {
} else if (childCtrl is ComboBox) {
} else if (childCtrl is ListBox) {
} else if (childCtrl is CheckBox) {

As the code stands now, it only handles the most common controls, and only saves the most important properties. It would be pretty easy to modify the code to handle other controls, and/or other properties, but the ones shown here were all I needed.

One point to note here is that the code does not save the list items for ComboBox or ListBox controls. This is because my application didn't populate these dynamically. If you need the list items saving as well, you would just use code very similar to the way the ListBox's SelectedIndex property is saved. As a ListBox can have more than one SelectedIndex, I used a loop to save them all. You could do the same with the list items if you needed this.

The Problem with SplitContainer Controls

After posting the initial version of this code, a comment was left (see below) that it didn't work with SplitContainer controls. As I had never used these, I had not spotted the problem.

It turns out that the SplitContainer control has two child Panels, which do not have a Name property. They are automatically named by the framework when you add the SplitContainer control to your form, but you can't set them yourself. As they don't have explicit names, the serialisation code was giving them an empty name when the XML file was written. This caused an error when the form was deserialised.

It turned out to be pretty easy to fix this, but it did require catching the SplitContainer control as a special case, which is something I don't like doing as a rule. All that was needed was when we serialise the form (at the end of the AddChildControls method), we need to check for the SplitContainer, and instead of serialising all child controls, pass the two Panel controls directly...

if (childCtrl is SplitContainer) {
  // handle this one as a special case
  AddChildControls(xmlSerialisedForm, ((SplitContainer)childCtrl).Panel1);
  AddChildControls(xmlSerialisedForm, ((SplitContainer)childCtrl).Panel2);
} else {
  AddChildControls(xmlSerialisedForm, childCtrl);

Then, when deserialising, if we are dealing with a SplitContainer control, the GetImmediateChildControl method needs to check for the parent of the parent, instead of just the parent...

private static Control GetImmediateChildControl(Control[] ctrl, Control currentCtrl) {
  Control c = null;
  for (int i = 0; i < ctrl.Length; i++) {
    if ((ctrl[i].Parent.Name == currentCtrl.Name)
     || (currentCtrl is SplitContainer && 
	ctrl[i].Parent.Parent.Name == currentCtrl.Name)) {
      c = ctrl[i];
  return c;

This fixed the problem.

Visible or Not Visible - That's a Very Interesting Question!

If you play with the test project, you'll see that clicking the "Are you happy?" CheckBox, causes the "happy" GroupBox to be shown or hidden. This is done with the following (pretty simple) event handler for the CheckBox's Click event:

private void chkHappy_CheckedChanged(object sender, EventArgs e) {
  grpHappy.Visible = chkHappy.Checked;

When I first wrote the code, I used the following line to serialise the Visible property for the current control:

xmlSerialisedForm.WriteElementString("Visible", childCtrl.Visible.ToString());

When I was playing with the test project included in the download, I noticed something odd. The test project has a child form that looks like this:


If you clear the "Are you happy?" CheckBox, the "happy" GroupBox is hidden. When the form is serialised, then deserialised, and the "Are you happy?" CheckBox checked, the form would look like this:


Notice that the "Why?" Label and the ComboBox next to it have both disappeared. Clicking the "Are you happy?" CheckBox shows and hides the GroupBox, but as the event handler code doesn't alter the visibility of the two child controls, they remain hidden. In a simple case like this, you easily get around the problem by manually setting the visibility of the child controls in the above event handler, but this has two drawbacks, one minor, and one significant.

The minor problem is that you have to write more code. This is little more than annoying, and shouldn't be necessary.

The major problem arises when you have child controls that may not necessarily be in the same state of visibility as the container. For example, in the application I was writing when I developed this serialisation code, I have many containers that contain subcontainers, or just individual controls whose visibility is set dynamically according to the state of other controls. In short, this means that some of the child controls will be visible, and some will not. There is no simple way to tell without running all of the relevant code again when the form is deserialised. This is just not practical in many cases, and would be very difficult to maintain in others.

After a lot of investigation, I discovered that when you set the Visible property of a container to false, the framework sets the Visible property of all contained controls to false as well. When the form is deserialised, all of these controls are hidden. When you click the CheckBox, only the GroupBox is made visible, leaving the other controls still hidden. This made me wonder how the framework knows the true state of the control's visibility, as without the serialisation/deserialisation issue, the child controls had their visibility set correctly when the container's visibility was set.

A friend of mine directed me to a discussion on the Stack Overflow site, where someone had asked a very similar question. An answer had been posted by someone who had followed the code up the control stack to see what the framework was actually doing. It seems that it uses an undocumented method called GetState with a parameter of 2 to get the true visibility.

I copied his code, and it worked fine. The serialisation of the Visible property now looks like this:

bool visible = (bool)typeof(Control).GetMethod("GetState",
                    BindingFlags.Instance | BindingFlags.NonPublic).Invoke(childCtrl,
                                                                 new object[] { 2 });
xmlSerialisedForm.WriteElementString("Visible", visible.ToString());

This is a little more complex, and uses an undocumented call (which makes me a little nervous), but it does work correctly.

The Problem of Multiple Controls with the Same Name

Whilst a Form, or any other container can only contain one control with the name TextBox1, there is nothing to stop a user control containing another control with the same name. As the enumeration of child controls used delves inside user controls (which is good, as it saves having to handle them separately), it does mean that at any point in the control hierarchy, you may have more than one control below you that has a particular name.

When deserialising, the code tries to find the right control to use with the following line:

Control[] ctrl = currentCtrl.Controls.Find(controlName, true);

If the control named controlName is the only one in this part of the control hierarchy with that name, then the ctrl array will only have one entry, and life is simple. However, if your form has a TextBox named TextBox1, and you have two instances of a user control on the form, and you added a TextBox to the user control and left the name as TextBox1, then you will have three controls in the hierarchy with the name TextBox1. The ctrl array will have three entries, and you have to decide which one is the right control.

My first solution to this was to count the length of the path up the hierarchy from ctrl[i] up to currentCtrl, and then choose the shortest one. After I wrote this, I then realised that (assuming the hierarchy has not changed between the form being serialised and deserialised), then the control you want must be an immediate child of the current container control. As only one control in this container can have the name you are looking for, the task of finding the right one just became much easier. All you do is loop through the ctrl array, looking for a control whose parent is the current container control. This was a much more elegant solution, and is encapsulated in the GetImmediateChildControl method.

What If Something Goes Wrong?

I considered this question quite a lot. My first version of the code had MessageBox calls in the cases where something went wrong, such as a control not being found during deserialisation, or the type in the XML file not matching the type of the control on the form. Obviously this is not a practical approach in general, as I wanted an all-purpose class that could be reused, and having MessageBoxes popping up isn't always the right thing to do (some would say it never is, but that's for another day). It was fine for development and debugging, but had to be changed for the final code.

I played around with throwing an exception in case something went wrong, but ended up throwing that idea out as it was getting too complex. In the end, I just ignored errors, meaning that if a control can't be found, or is the wrong type, then nothing is done. For me, this was the best solution. For people working in big teams, where you have little or no control over what other developers do, it may be better to add exceptions to this code, and warn other developers to catch them.

In practice, as long as the XML file can be read and written without problem, I never encountered any exceptions that weren't caused by my own bugs. Once I had those fixed, the code ran without error. The only time I can foresee any problems is if the control hierarchy changes, and you attempt to deserialise the form from an outdated XML file. In this case, some controls may not be repopulated. Next time the form is serialised, the data will be written correctly.


The test project included in the download was written using Visual C# 2010 Express. If you can't open it in your version of Visual Studio, just copy the FormSerialisor.cs file into your project, and follow the instructions above to use it with your own test project. You don't need anything fancy. The code has been tested with the 2.0 and 4.0 versions of the .NET Framework and worked fine with both.


  • 8th June '10: Initial article written
  • 13th June '10: Code updated to cope with the odd behaviour of a SplitContainer (see the comment "controls in SplitContainer" below). I also added the FormSerialisor.cs file as a separate download for those who either don't have Visual C# 2010 Express, or who don't want the test project


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


About the Author

Mr Yossu
Software Developer Pixata Ltd
United Kingdom United Kingdom
I've been writing programs for about thirty years now, having started out on an IBM System/32, and moving my way through various IBM mainframes, then onto SGI Unix machines, and ending up (so far) with Windows and the .NET framework.

Favourite programming language is C#, favourite music is classical (the two go well together).

You may also be interested in...


Comments and Discussions

GeneralСпасибо!!! Pin
Member 1200719924-Sep-15 13:17
memberMember 1200719924-Sep-15 13:17 
GeneralLimited and very basic foundation Pin
Rf L28-Oct-14 12:50
memberRf L28-Oct-14 12:50 
GeneralRe: Limited and very basic foundation Pin
Rf L28-Oct-14 14:01
memberRf L28-Oct-14 14:01 
Questionhow to exclude a textBox with a password Pin
domalain22-Oct-14 23:39
memberdomalain22-Oct-14 23:39 
GeneralMy vote of 2 Pin
b0bi24-Sep-14 6:06
memberb0bi24-Sep-14 6:06 
QuestionSerializing Dynamic Controls Pin
iamHD20-Sep-14 7:07
memberiamHD20-Sep-14 7:07 
QuestionSaving textbox color and read text box color Pin
Member 1044643915-Jul-14 23:38
memberMember 1044643915-Jul-14 23:38 
QuestionThanks and some issues Pin
Member 1081387412-May-14 19:27
memberMember 1081387412-May-14 19:27 
QuestionThis saved me so much time and effort; though, it doesn't like scroll bars, hard failure Pin
Paul Hugo21-Feb-14 8:43
memberPaul Hugo21-Feb-14 8:43 
GeneralMy vote of 5 Pin
Jayanta Chatterjee10-Dec-13 19:52
memberJayanta Chatterjee10-Dec-13 19:52 
QuestionWill it work for forms with GridViews and ListBoxes and ListViews which are databound? Pin
renu.iitkgp26-Nov-13 5:53
memberrenu.iitkgp26-Nov-13 5:53 
QuestionNumericUpDown fix Pin
Member 973002612-Sep-13 20:37
memberMember 973002612-Sep-13 20:37 
AnswerRe: NumericUpDown fix Pin
Member 1081387413-May-14 9:55
memberMember 1081387413-May-14 9:55 
GeneralRe: NumericUpDown fix Pin
Member 974692910-Jun-14 1:58
memberMember 974692910-Jun-14 1:58 
GeneralRe: NumericUpDown fix Pin
Member 973002617-Dec-14 4:31
memberMember 973002617-Dec-14 4:31 
QuestionMy Vote of 5 Pin
Wusiji29-May-13 7:10
memberWusiji29-May-13 7:10 
QuestionC#: Saving the state (serializing) a Windows Form (View) for DataGridView control Pin
vishwas6030@gmail.com6-May-13 4:39
membervishwas6030@gmail.com6-May-13 4:39 
AnswerRe: C#: Saving the state (serializing) a Windows Form (View) for DataGridView control Pin
Mr Yossu6-May-13 4:56
memberMr Yossu6-May-13 4:56 
Questionnot bad Pin
CIDev6-Feb-13 5:00
memberCIDev6-Feb-13 5:00 
GeneralMy vote of 5 Pin
werner.keilholz31-Jan-13 0:26
memberwerner.keilholz31-Jan-13 0:26 
QuestionWPF version Pin
yolki201217-Nov-12 1:03
memberyolki201217-Nov-12 1:03 
AnswerRe: WPF version Pin
Mr Yossu18-Nov-12 5:28
memberMr Yossu18-Nov-12 5:28 

No, this won't work as-is with WPF, it was written for WinForms. However, I don't think it would be too hard to do something similar for WPF. You would have to change the code that enumerates the children of the form, and then decide which are the main proeprties you would want to add into the XML file. I haven't tried it (no time, and don't need it at the moment), but I don't think it would be too hard.

Hope this helps
GeneralMy vote of 5 Pin
Farhan Ghumra30-Aug-12 21:47
memberFarhan Ghumra30-Aug-12 21:47 
QuestionSerialising and deSerialising that i've added new buttons through code on program run Pin
defterniko17-Jul-12 7:57
memberdefterniko17-Jul-12 7:57 
AnswerRe: Serialising and deSerialising that i've added new buttons through code on program run Pin
Mr Yossu17-Jul-12 13:06
memberMr Yossu17-Jul-12 13:06 
Questionyou've missed the CheckedListbox (realy good post! thank you) Pin
timmie2521516-Apr-12 6:22
membertimmie2521516-Apr-12 6:22 
BugScrollBars of DataGridview Pin
pagans_son4-Apr-12 9:24
memberpagans_son4-Apr-12 9:24 
GeneralRe: ScrollBars of DataGridview Pin
ExoCoding22-Jun-14 23:04
memberExoCoding22-Jun-14 23:04 
GeneralMy vote of 5 Pin
pagans_son4-Apr-12 4:10
memberpagans_son4-Apr-12 4:10 
GeneralMy vote of 5 Pin
Member 850460810-Jan-12 9:31
memberMember 850460810-Jan-12 9:31 
QuestionRadio buttons Pin
Member 850460810-Jan-12 4:34
memberMember 850460810-Jan-12 4:34 
AnswerRe: Radio buttons Pin
Mr Yossu11-Jan-12 6:35
memberMr Yossu11-Jan-12 6:35 
GeneralRe: Radio buttons Pin
Member 850460811-Jan-12 6:37
memberMember 850460811-Jan-12 6:37 
GeneralRe: Radio buttons Pin
Vader_Oz10-Feb-16 16:55
memberVader_Oz10-Feb-16 16:55 
QuestionA ComboBox with a DataBinding? Pin
nyhack566-Sep-11 6:19
membernyhack566-Sep-11 6:19 
AnswerRe: A ComboBox with a DataBinding? Pin
Mr Yossu6-Sep-11 7:25
memberMr Yossu6-Sep-11 7:25 
GeneralRe: A ComboBox with a DataBinding? Pin
nyhack566-Sep-11 7:52
membernyhack566-Sep-11 7:52 
GeneralListView Control Pin
gyanaccess1-Nov-10 21:05
membergyanaccess1-Nov-10 21:05 
GeneralMy vote of 5, and thank you Pin
BillWoodruff15-Jun-10 19:57
memberBillWoodruff15-Jun-10 19:57 
GeneralRe: My vote of 5, and thank you Pin
Mr Yossu16-Jun-10 3:48
memberMr Yossu16-Jun-10 3:48 
GeneralMy vote of 2 Pin
Simon Hughes14-Jun-10 11:02
memberSimon Hughes14-Jun-10 11:02 
GeneralRe: My vote of 2 Pin
Mr Yossu15-Jun-10 4:52
memberMr Yossu15-Jun-10 4:52 
GeneralNot a pattern to recommend Pin
Mihai Maerean14-Jun-10 10:21
memberMihai Maerean14-Jun-10 10:21 
GeneralRe: Not a pattern to recommend Pin
Mr Yossu15-Jun-10 4:57
memberMr Yossu15-Jun-10 4:57 
Generalvery useful Pin
Omar Gamil13-Jun-10 22:16
memberOmar Gamil13-Jun-10 22:16 
GeneralRe: very useful Pin
Mr Yossu14-Jun-10 3:39
memberMr Yossu14-Jun-10 3:39 
Generalcontrols in SplitContainer Pin
Member 211133911-Jun-10 3:18
memberMember 211133911-Jun-10 3:18 
GeneralRe: controls in SplitContainer Pin
Mr Yossu13-Jun-10 5:23
memberMr Yossu13-Jun-10 5:23 
GeneralRe: controls in SplitContainer Pin
Mr Yossu13-Jun-10 5:49
memberMr Yossu13-Jun-10 5:49 
General"visible" property Pin
supercat99-Jun-10 12:06
membersupercat99-Jun-10 12:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    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
Web02 | 2.8.170118.1 | Last Updated 13 Jun 2010
Article Copyright 2010 by Mr Yossu
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid