Introduction
This sample program demonstrates how to save and restore DataGridView
column widths, order and visibility using the Application Settings feature of Windows Forms. Application Settings provide an easy way to deal with persistent data. An Access database is included with sample data that is displayed by the DataGridView
control.
You will be guided through creating an application that uses a DataGridView
control to display, add, update and delete data using a sample database.
This sample was developed in Microsoft Visual C# 2005 Express Edition (free download). The sample database was created using Microsoft Access.
Create a Database
Use either Microsoft Access or Microsoft SQL Server 2005 Express Edition (free download) to create a database. For the sake of simplicity, this example uses a database created with Access. The steps outlined in this example are similar to the ones needed when using an SQL Server database.
This example will use a database of tradespeople in the construction industry. The following image shows the Microsoft Access table definition.
Create a C# Project
Create a new C# project using the Windows Application template. Add the database to your project by selecting Project | Add Existing Item. Change the 'Files of type' to 'Data Files' and add the database file you just created to the project. When the Data Source Configuration Wizard appears, check the Tables checkbox to include the table and all its columns. Click Finish to accept the choices and dismiss the wizard.
Show the data source by selecting Data | Show Data Sources. Click on the refresh icon in the Data Sources pane and make sure the Contractors table has a DataGridView
icon as shown in the following image. Select DataGridView
from the dropdown box if needed.
Add a DataGridView
control to the form by dragging the Contractors
table from Data Sources onto the form. You'll notice that a navigator toolbar was also added to the form at the same time. Resize the form and DataGridView
control to your liking. The results should look similar to the following image.
Now build and run the application. Notice that the DataGridView
control doesn't change if you resize the window. Also note that you can change column widths but cannot reorder the columns. Close the application and we'll address these issues.
Right-click the DataGridView
control and select Properties. Find the AllowUserToOrderColumns
property and set it to True
. Now find the Anchor
property and select Bottom and Right. Note that Top and Left should already be selected.
Adding Database Entries
Rebuild and launch the application. Make sure that the DataGridView
control changes size when the window is resized. Also check that the columns can indeed be dragged to new positions.
Type entries in the Name
, Trade
, Phone
and Comments
columns. Add multiple rows if you'd like to. When you're finished, click on the save (floppy disk) icon in the navigator toolbar to save the changes. Now exit and re-launch the application. Where is the data you just entered?
By default Visual Studio makes a copy of the database and places it in the bin\Debug directory. Any changes you make to the database get overwritten when the application is launched. To change this behavior, begin by right-clicking on the database (Contractors.mdb) in the Solution Explorer pane and selecting Properties. Change the Copy to Output Directory property to Do not copy. Now use Windows Explorer to make a copy of the database and paste it into the bin\Debug directory.
Launch the application and add some entries again. Make sure to click the save icon before exiting so the changes are saved. Now exit and re-launch to verify that the entries were saved.
Application Settings File
Display the Solution Explorer pane by selecting View | Solution Explorer. When a project is created, a Settings.settings file is automatically added to the project. Expand the Properties folder in the Solution Explorer pane and double-click on Settings.settings. When we added the Contractors
data source to the project, the database connection string was automatically added to the application settings. You'll see this as the first entry in the application settings editor.
Persistent data is saved in the application settings file when the application is closed, and restored when the application is launched. We are going to save the size and location of the application's frame and the columns' display index, width, visibility and column index for the DataViewGrid
control.
Three entries need to be added to the application settings file so this information can be saved. We'll save the frame's location to DataViewGridFormLocation
, so type that into the Name
column. Now select System.Drawing.Point
in the type column.
By convention, values in the Application scope are read-only, and values in the User scope are read-write. Since we'll be saving persistent values each time the application closes, all our entries will be in the User scope.
Now add DataViewGridFormSize
as type System.Drawing.Point
in the User scope and DataViewGridFormColumns
as type System.Collections.Specialized.StringCollection
in the User scope. Leave the Value
column blank for all three of the new entries. Save and close the Settings.settings file.
Saving Persistent Data
Without writing a single line of code, we have an application that displays data from a database with the ability to add, update and delete entries. Not too bad. Now it's time to write some code.
Click on the application's form and select View | Properties Window to view its properties. Display the form's events by clicking on the lightning bolt in the Properties pane. Now double-click on the FormClosing
property to automatically name and add an event handler. When the user closes the form, typically when the application is exiting, the PersistentDataGridViewForm_FormClosing
event handler is executed.
To save the persistent data, add the following code to the event handler you just created.
private void PersistentDataGridViewForm_FormClosing
(object sender, FormClosingEventArgs e) {
StringCollection stringCollection = new StringCollection();
int i = 0;
foreach(DataGridViewColumn column in this.contractorsDataGridView.Columns) {
stringCollection.Add(string.Format(
"{0},{1},{2},{3}",
column.DisplayIndex.ToString("D2"),
column.Width,
column.Visible,
i++));
}
Properties.Settings.Default.DataGridViewFormColumns = stringCollection;
if(this.WindowState == FormWindowState.Normal) {
Properties.Settings.Default.DataGridViewFormLocation = this.Location;
Properties.Settings.Default.DataGridViewFormSize = this.Size;
}
else {
Properties.Settings.Default.DataGridViewFormLocation =
this.RestoreBounds.Location;
Properties.Settings.Default.DataGridViewFormSize = this.RestoreBounds.Size;
}
Properties.Settings.Default.Save();
}
You'll also need to add the following using
statement at the beginning of the file. The using
statement is needed to access the string
collection class.
using System.Collections.Specialized;
As you'll recall, we added an entry to the Settings.settings file of type stringCollection
for saving DataGridView
column state information. The column's state is represented by four comma-separated values, and a collection of these comma separated values is used to save the data for all columns. The code adds a string
to the collection for each column of the DataGridView
control. Typical column data looks like this: 01,100,True,0
.
Next the form's location and size are written to persistent storage. If the form is in a minimized or maximized state, the location and size must be read from the RestoreBounds
property.
The data is written to a file to make it available to the application the next time it is launched by the last line of code in the event handler.
Now that the state information is saved when the application exits, we need to read this data to initialize the form to its previous state the next time the application is launched.
The code that restores the application's state is placed in the form's constructor as follows. Note that the code is placed after the call to InitializeComponent
.
public PersistentDataGridViewForm() {
InitializeComponent();
try {
Point location = Properties.Settings.Default.DataGridViewFormLocation;
Size size = Properties.Settings.Default.DataGridViewFormSize;
this.StartPosition = FormStartPosition.Manual;
this.Location = location;
this.Size = size;
StringCollection cols = Properties.Settings.Default.DataGridViewFormColumns;
string[] colsArray = new string[cols.Count];
cols.CopyTo(colsArray, 0);
Array.Sort(colsarray);
for(int i = 0; i < colsArray.Length; ++i) {
string[] a = colsArray[i].Split(',');
int index = int.Parse(a[3]);
this.contractorsDataGridView.Columns[index].DisplayIndex = Int16.Parse(a[0]);
this.contractorsDataGridView.Columns[index].Width = Int16.Parse(a[1]);
this.contractorsDataGridView.Columns[index].Visible = bool.Parse(a[2]);
}
}
catch(NullReferenceException) {
}
}
Recall that we left the value columns blank for the entries we added to Settings.settings. The first time we run the application a NullReferenceException
will be generated because these values are empty.
After the location
and size
values have been read without having a NullReferenceException
thrown, the form is notified that it will be manually positioned by assigning Manual
to the StartPosition
property.
Next, each line of column values gets split into four string
s and saved in a string
array. Each value is then parsed and assigned to the specified column.
The column data must first be sorted in display index order. In certain cases, the column order will not be displayed as expected if the columns' DisplayIndex
property is not assigned in ascending order. This occurs because the display indices are renumbered to avoid any gaps (e.g. 0, 1, 3, 4 would be renumbered to 0, 1, 2, 3). Note that because a simple string
sort is used, leading zeros are needed so the string
representations of DisplayIndex
numbers are all the same length (e.g. 01, 02, 03, ... as opposed to 1, 10, 2, 3, ...).
There is one more change that needs to be done to the application. The ID column displays a number generated automatically by the database, which is of little interest to us. In the DataGridView
control Properties pane, locate the Columns
property. Click in the value column. Now click on the ellipse button that appears, to edit the columns' properties. In the Edit Columns dialog, select the ID
column and set its Visible
property to False
. Click OK to accept the change and dismiss the Edit Columns dialog.
Launch the application and change its location and size. Next, reorder the columns and change some column sizes. Now exit and re-launch the application to verify that its previous state is restored.
Where does the persistent data get stored? Under Windows XP it gets saved in \Documents and Settings\user_name\Local Settings\Application Data\company_name\mangled_program_name\version_number in a file named user.config. The location can change depending on the operating system. For example, under Vista the file is saved in \Users\user_name\AppData\Local\company_name\mangled_program_name\
version_number. During development, several user.config files can be created which can lead to some confusion. Notice in the Settings.settings
editor, there is a button in the upper left of the screen named Synchronize. Click this button to delete all copies of user.config. What's nice is that a list is given of places where the user.config can exist. Once you've saved persistent data, locate and examine a user.config file to see what the saved data looks like.
What's Next?
I've demonstrated how to save and restore form and column state information; however I have just scratched the surface. You may wish to refer to Microsoft documentation for further information on Application Settings.
The following tasks are left to the reader as exercises.
Add a context menu to the DataGridView
control to select which columns are displayed by setting the Visible
property to either True
or False
.
During application development, it is likely that columns will be added or deleted from the DataGridView
control. After such a change, an exception will be generated in the for
loop used to restore the columns' state. Add code to check for this condition.
Should the format of the persistent data change or get corrupted, an exception would most likely be thrown when Parse
is called in the for
loop. You may wish to add code to deal with this.
History
1.0.0.0 (October 2007)
1.1.0.0 (March 2009)
- Corrected the code to fix the column order issue as reported by Kevin Van Puyvelde and krt12
- Updated the article to explain the code addressing the column order issue
- Indicated where the user.config file is located under Vista