Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

GridMemory: Grid that remembers its columns' width

0.00/5 (No votes)
23 Jan 2005 1  
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.

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

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.

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.

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.

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