Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

DataGridView with Persistent Column Widths and Order

4.70/5 (28 votes)
27 Mar 2009CPOL9 min read 1   4.2K  
An application demonstrating how to save column width, order and visibility information for a DataGridView control, bound to an Access database, using the Windows Forms Application Settings feature
Screenshot - Demo Application.png

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.

Contractor's database table

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.

Data Sources pane

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.

Data Sources pane

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.

C#
private void PersistentDataGridViewForm_FormClosing
		(object sender, FormClosingEventArgs e) {
    // Save column state data
    // including order, column width and whether or not the column is visible
    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;

    // Save location and size data
    // RestoreBounds remembers normal position if minimized or maximized
    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;
    }

    // Save the data
    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.

C#
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.

C#
public PersistentDataGridViewForm() {
    InitializeComponent();

    // Restore state data
    try {
        Point location = Properties.Settings.Default.DataGridViewFormLocation;
        Size size = Properties.Settings.Default.DataGridViewFormSize;

        // Set StartPosition to manual
        // after being sure there are no null values
        this.StartPosition = FormStartPosition.Manual;
        this.Location = location;
        this.Size = size;

        // Restore the columns' state
        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) {
        // This happens when settings values are empty
    }
}

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 strings 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)

  • First release

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

License

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