Click here to Skip to main content
15,901,035 members
Articles / Desktop Programming / Windows Forms
Article

GridMemory: Grid that remembers its columns' width

Rate me:
Please Sign up or sign in to vote.
1.77/5 (6 votes)
23 Jan 20053 min read 52.3K   424   36   3
A DataGrid that stores its last state in XML file and loads the last state of the columns' width the next time the application is loaded.

Sample Image - gridmemory.jpg

Introduction

One of the features that is good to have when you work with a DataGrid control is to make it remember its columns' width. In this article, I will be presenting a way to make your grid control remember its columns' size. Basically, somehow you need to make the grid control ‘memory’ linked.

Today, XML has been widely gaining acceptance in industry. I, therefore, will use it to function as memory to the grid control. In this article, I would be presenting an XML file that can remember a single data grid control columns width. The extension of this article would be to make it remembering more than one state from multiple grid controls.

Using the code

First of all, you will need to add a DataGridTableStyle to the grid control before you bind the grid control to a DataSource. This tablestyle will be used to set the columns' width through its Width property. So, every time the application get loaded, the last column state would be fetched and loaded.

The following example shows you a hard-coded way of setting the columns' width of your grid control. This way you can’t expect the grid control to remember its state.

C#
public void HardcodedInitializeGrid()
{
    Hashtable colWidths = LoadLastGridState();

    // develop datatable schema
    tAddress = new DataTable();
    tAddress.TableName = "tAddress";
    tAddress.Columns.Add("No", typeof(int));
    tAddress.Columns.Add("Firstname", typeof(string));
    tAddress.Columns.Add("Lastname", typeof(string));
    tAddress.Columns.Add("Email", typeof(string));        

    // fill data row with record #1
    DataRow newRow;
            
    newRow = tAddress.NewRow();
    newRow["No"] = 0;
    newRow["Firstname"]    = "Henry";
    newRow["Lastname"]    = "Tan";
    newRow["Email"]    = "henryws@it.uts.edu.au";

    // add record #1 to the tAddress
    tAddress.Rows.Add(newRow);

    // fill data row with record #2
    newRow = tAddress.NewRow();
    newRow["No"] = 1;
    newRow["Firstname"]    = "Albert";
    newRow["Lastname"]    = "Einstein";
    newRow["Email"]    = "albert.einstein@heaven.au";

    // add record #2 to the tAddress
    tAddress.Rows.Add(newRow);
    DataGridTableStyle tablestyle = new DataGridTableStyle();
    tablestyle.AlternatingBackColor = Color.WhiteSmoke;
    tablestyle.MappingName = "tAddress";

    // initialize tAddress style: set columns width
    // use DataGridTextBoxColumn, display textbox on each column
    DataGridTextBoxColumn textboxColumn = null;
    for(int i=0; i < tAddress.Columns.Count; i++)
    {
        string colname = tAddress.Columns[i].ColumnName;
        if(colname.Equals("No"))
        {
            textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
            textboxColumn.Width = int.Parse(colWidths["No"].ToString());    
            tablestyle.GridColumnStyles.Add(textboxColumn);
        }
        else if(colname.Equals("Firstname"))
        {
            textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
            textboxColumn.Width = int.Parse(colWidths["Firstname"].ToString());
            tablestyle.GridColumnStyles.Add(textboxColumn);
        }
        else if(colname.Equals("Lastname"))
        {
            textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
            textboxColumn.Width = int.Parse(colWidths["Lastname"].ToString());
            tablestyle.GridColumnStyles.Add(textboxColumn);
        }
        else if(colname.Equals("Email"))
        {
            textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
            textboxColumn.Width = int.Parse(colWidths["Email"].ToString());
            tablestyle.GridColumnStyles.Add(textboxColumn);
        }
        else // default column with default size
        {
            textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = "Default";
            textboxColumn.MappingName = "Default";
            textboxColumn.Width = int.Parse(colWidths["Default"].ToString());
            tablestyle.GridColumnStyles.Add(textboxColumn);
        }
    }

    // add the tAddressstyle to the grid
    dataGrid.TableStyles.Add(tablestyle);

    // bind the grid with the datatAddress
    dataGrid.DataSource = tAddress;
}

Figure 1: Hardcoded way of setting columns' width.

The hard-coded approach is not what we usually want in the real application. What we want is an application that can memorize each of the columns' width. The above code, creates a DataTable with four columns, “No”, “Firstname”, “Lastname”, and “Email”. It then adds two records to the table. Next, creates DataGridTableStyle object, setting the grid’s AlternatingBackColor with WhiteSmoke, and map it with the previously created table by setting the MappingName equal to the table’s name. Next, we need to construct an XML file to store the grid columns' width state. The following is one possible format.

XML
<?xml version="1.0" encoding="utf-8"?>
<GridState>
  <Control name="MemoryGrid">
    <Column name="No">30</Column>
    <Column name="FirstName">200</Column>
    <Column name="LastName">200</Column>
    <Column name="Email">250</Column>
    <Column name="Default">100</Column>
  </Control>
</GridState>

Figure 2: XML file to remember columns' width.

As now, the columns' widths are stored in the XML format, consequently, some changes need to be made to the previous code in fig. 1. You need to read the columns' width of each of the column and set it for each of the column. For readability purpose, all changes made to the previous code in fig 1. would be highlighted in red.

C#
public void InitializeGridWithLastState()
{
    Hashtable colWidths = LoadLastGridState();

    // develop datatable schema
    tAddress = new DataTable();
    tAddress.TableName = "tAddress";
    tAddress.Columns.Add("No", typeof(int));
    tAddress.Columns.Add("Firstname", typeof(string));
    tAddress.Columns.Add("Lastname", typeof(string));
    tAddress.Columns.Add("Email", typeof(string));        

    // fill data row with record #1
    DataRow newRow;
            
    newRow = tAddress.NewRow();
    newRow["No"] = 0;
    newRow["Firstname"]    = "Henry";
    newRow["Lastname"]    = "Tan";
    newRow["Email"]    = "henryws@it.uts.edu.au";

    // add record #1 to the tAddress
    tAddress.Rows.Add(newRow);

    // fill data row with record #2
    newRow = tAddress.NewRow();
    newRow["No"] = 1;
    newRow["Firstname"]    = "Albert";
    newRow["Lastname"]    = "Einstein";
    newRow["Email"]    = "albert.einstein@heaven.au";

    // add record #2 to the tAddress
    tAddress.Rows.Add(newRow);
    DataGridTableStyle tablestyle = new DataGridTableStyle();
    tablestyle.AlternatingBackColor = Color.WhiteSmoke;
    tablestyle.MappingName = "tAddress";

    // initialize table style: set columns width
    // use DataGridTextBoxColumn, display textbox on each column
    DataGridTextBoxColumn textboxColStyle = null;
    for(int i=0; i < table.Column.Count; i++)
    {
        string colname = table.Column[i].ColumnName;
           if(colname.Equals("No"))
           {
              textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
              textboxColumn.Width = colWidths["No"];    
           }
           else if(colname.Equals("Firstname"))
           {
               textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
              textboxColumn.Width = colWidths["Firstname"];
           }
           else if(colname.Equals("Lastname"))
           {
              textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
              textboxColumn.Width = colWidths["Lastname"];
           }
           else if(colname.Equals("Emails"))
           {
              textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
              textboxColumn.Width = colWidths["Emails"];
           }
           else // default column with default size
           {
              textboxColumn = new DataGridTextBoxColumn();
            textboxColumn.HeaderText = colname;
            textboxColumn.MappingName = colname;
              textboxColumn.Width = colWidths["Default"];
           }
    }

    // add the tablestyle to the grid
    dataGrid.TableStyles.Add(tablestyle);

    // bind the grid with the datatable
    dataGrid.DataSource = tAddress;
}

Figure 3: Initialize with the last grid's state [columns' width].

Few changes easily noticeable from the code in fig. 3. Instead of assigning the columns' width with a constant, it fetches the columns width from the XML file. Using hashtable makes the task becomes handy. We store the pairs of {column-name, column-width} in the hashtable. For example, in fig 3. colWidths[“Firstname”] will return its column width. Oppositely, to add a pair to the hashtable, colwidths.Add(“Firstname”, 200) will do the job, storing a pair with “Firstname” as the key and 200 as the value.

C#
public Hashtable LoadLastGridState()
{
    Hashtable colWidths = new Hashtable();
    
    // using XmlTextReader to read the gridstate from gridstate.xml
    XmlTextReader tr = new XmlTextReader("gridstate.xml");
            
    // traverse the xml document using text reader
    while(tr.Read())
    {
          if(tr.NodeType == XmlNodeType.Element)
          {
                if(tr.Name == "Column")
             {
              string key = tr.GetAttribute("name");
                  string val = tr.ReadElementString();
                  colWidths.Add(key, val);
            }
          }
    }
    tr.Close();
    return colWidths;
}

Figure 4: Loads columns' width from XML file.

Loading the grid’s state from XML file is quite straight-forward. Especially, if you are already familiar with XmlTextReader. For each of the element in XML that started with “Column” as its tag-name, fetch its attribute name (column name) and the column width. Add it to the hashtable object and return the hashtable object after completing reading the XML.

C#
public void SaveCurrentGridState()
{
    if(dataGrid.TableStyles.Count == 0)
        return;

    Hashtable colWidths = new Hashtable();

    XmlTextWriter tw = 
         new XmlTextWriter("gridstate.xml", System.Text.Encoding.ASCII);
    tw.Formatting = Formatting.Indented;

    // get the GridColumnStylesCollection from the dataGrid control
    GridColumnStylesCollection colstyle = 
        dataGrid.TableStyles[0].GridColumnStyles;
                
    int test = colstyle["No"].Width;
    colWidths.Add("No", colstyle["No"].Width);
    colWidths.Add("Firstname", colstyle["Firstname"].Width);
    colWidths.Add("Lastname", colstyle["Lastname"].Width);
    colWidths.Add("Email", colstyle["Email"].Width);

    #region Document
    tw.WriteStartDocument();

    #region GridState
    tw.WriteStartElement("GridState");

    #region gridMainOptStrategy
    tw.WriteStartElement("Control");
    tw.WriteAttributeString("name", "MemoryGrid");
    
    #region No
    tw.WriteStartElement("Column");
    tw.WriteAttributeString("name", "No");
    tw.WriteString(colWidths["No"].ToString());
    tw.WriteEndElement();
    #endregion
    
    #region Firstname
    tw.WriteStartElement("Column");
    tw.WriteAttributeString("name", "Firstname");
    tw.WriteString(colWidths["Firstname"].ToString());
    tw.WriteEndElement();
    #endregion

    #region Lastname
    tw.WriteStartElement("Column");
    tw.WriteAttributeString("name", "Lastname");
    tw.WriteString(colWidths["Lastname"].ToString());
    tw.WriteEndElement();
    #endregion

    #region Email
    tw.WriteStartElement("Column");
    tw.WriteAttributeString("name", "Email");
    tw.WriteString(colWidths["Email"].ToString());
    tw.WriteEndElement();
    #endregion

    tw.WriteEndElement();
    #endregion

    tw.WriteEndElement();
    #endregion

    tw.WriteEndDocument();
    #endregion

    tw.Close();
}

Figure 5: Saves current grid's columns' width into XML file.

Finally, at some point, you need to call the SaveCurrentGridState(). Otherwise the grid won’t be able to call the current state in a future time. You can save the grid’s state at closing event when the application is closed.

C#
private void MainForm_Closed(object sender, System.EventArgs e)
{
    SaveCurrentGridState();
}

Figure 6: Closed event handler.

That’s it! Now, you can have your grid control remembering its columns' width at all time. Once you close the application and load it again, you won’t notice anything difference. Just few notes before I conclude the article, you can extend the XML format so that it can remember more than one grid controls. What you need to do is to structure the grid states as in the following XML file:

XML
<?xml version="1.0" encoding="utf-8"?>
<GridState>
  <Control name="Grid1">
    <Column name="No">50</Column>
    <Column name="FirstName">200</Column>
    <Column name="LastName">200</Column>
    <Column name="Email">250</Column>
    <Column name="Default">100</Column>
  </Control>
  <Control name="Grid2">
    <Column name="Id">30</Column>
    <Column name="Item Description">200</Column>
    <Column name="Price">200</Column>
    <Column name="Default">100</Column>
  </Control>
</GridState>
Figure 7: XML remember many controls' state

Points of Interest

You need to make few adjustments to the LoadLastGridState() and SaveCurrentGridState() which I will write on my next articles.

History

First release - Memorizing one grid columns' width.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer
United States United States
Henry Tan was born in a small town, Sukabumi, Indonesia, on December 7th, 1979. He obtained his Bachelor of Computer System Engineering with first class honour from La Trobe University, VIC, Australia in 2003. During his undergraduate study, he was nominated as the most outstanding Honours Student in Computer Science. Additionally, he was the holder of 2003 ACS Student Award. After he finished his Honour year at La Trobe University, on August 2003, he continued his study pursuing his doctorate degree at UTS under supervision Prof. Tharam S. Dillon. He obtained his PhD on March 2008. His research interests include Data Mining, Computer Graphics, Game Programming, Neural Network, AI, and Software Development. On January 2006 he took the job offer from Microsoft Redmond, USA as a Software Design Engineer (SDE).


What he like most? Programming!Math!Physisc!Computer Graphics!Playing Game!

What programming language?
C/C++ but started to love C#.

Comments and Discussions

 
Questionforeach?? Pin
Corneliu Tusnea23-Jan-05 11:57
Corneliu Tusnea23-Jan-05 11:57 
Hi,
I truly recommend start using "foreach" and get rid of all the code you did to set/get/whatever for each column.
What will you do when you have a grid with 180 columns?
... purgatory?
AnswerRe: foreach?? Pin
horcas9-Aug-05 4:54
horcas9-Aug-05 4:54 
GeneralSuggestion for improvement Pin
mav.northwind23-Jan-05 5:07
mav.northwind23-Jan-05 5:07 

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.