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

Auto sizing DataGrid

0.00/5 (No votes)
12 Nov 2003 1  
A DataGrid that is able to resize its last column.

Introduction

Most of the projects that I build with C# are either console based or class libraries. Lately I have been building some graphical applications too. I have some experience building graphical applications using the Java - Swing interface, so I expected the transition to C# would be easy to make. One of the things I found very interesting in Java is the use of layout managers, and the great ability to customize the views of controls on the form.

C# does not make any use of layout managers, but relies on absolute positioning. This �feature� is also available in Java but is of course not preferred, the use of layout managers works much better for me. Also the second strength of Java, it�s customizability, is something that I am missing in .NET. Various things that were easy to implement in Java are a lot harder in C#. In this article, I will show how to build around a rather small problem of a specific Windows Forms control, the DataGrid.

I found that the DataGrid component lacks some properties I desperately want. The problem that will be tackled here is the size of the last column. When the size of the table that is being shown is smaller than the ClientSize of the DataGrid, the last column will not resize to fit the whole area. The image below shows the problem.

The red arrow �A� is pointing to the area I am after. I want the grid to automatically resize it�s last column, or all columns a little bit, so the DataGrid fills the indicated space. This can be done, but it�s not exactly elegant. Currently my implementation is only available for DataTables as the DataSource, porting should be pretty easy however. Now for the actual work..

The code

After some searching, I found that the best way to tackle the problem, was to build a customized DataGridTableStyle. I will call it AutoResizeDataGridTableStyle, what�s in a name! This class will perform the following things:

  1. Look pretty, the default style doesn�t.
  2. Detect changes in the DataGrid�s DataSource.
  3. Detect changes in the DataGrid�s Size.

Implementing this will not be hard, detecting the changes can be done using the events that the DataGrid spits out. This will make the AutoResizeDataGridTableStyle a bit ugly because we need to register the events. This means we can�t just add a default instance of AutoResizeDataGridTableStyle to the TableStyles collection of the DataGrid.

Looking pretty

The main reason for creating a DataGridTableStyle is to make the DataGrid look nice. We will add some prettiness to the constructor. The variable OFFSET_GRID will be explained further on.

public class AutoResizeDataGridTableStyle: DataGridTableStyle
{
    private int OFFSET_GRID = 39;

    public AutoResizeDataGridTableStyle(): base()
    {
        BackColor = Color.WhiteSmoke;
        AlternatingBackColor = Color.Lavender;
        ForeColor = Color.MidnightBlue;
        GridLineColor = Color.Gainsboro;
        HeaderBackColor = Color.MidnightBlue;
        HeaderForeColor = Color.WhiteSmoke;
        LinkColor = Color.Teal;
        SelectionBackColor = Color.CadetBlue;
        SelectionForeColor = Color.WhiteSmoke;
        ColumnHeadersVisible = true;
        RowHeadersVisible = true;
        HeaderFont = new Font("Microsoft Sans Serif", 8);
    }

OnDataSourceChanged

We need to detect changes in the DataSource of the DataGrid to be able to add DataGridColumnStyles to the DataGridTableStyle. The class does not support any advanced handling for these styles, it just builds normal DataGridTextBoxColumns. As you can see, this method only works for DataTables. If all this is gibberish to you, read the manual about DataGrid, it's not the best but it's a start.

public void OnDataSourceChanged(object sender, EventArgs e)
{
    GridColumnStyles.Clear();
    if(DataGrid != null && DataGrid.DataSource != null &&
        DataGrid.DataSource is DataTable)
    {
        DataTable currentTable = (DataTable)DataGrid.DataSource;
        foreach(DataColumn column in currentTable.Columns)
        {
            DataGridColumnStyle style = new DataGridTextBoxColumn();
            style.HeaderText = column.ColumnName;
            style.MappingName = column.ColumnName;
            GridColumnStyles.Add(style);
        }
    }
    // Call the eventhandler for resize events

    OnDataGridResize(this,new EventArgs());
}

The method simply runs through each column in the DataTable, creates a new DataGridTextBoxColumn for it, and finally resizes the DataGrid�s columns.

OnDataGridResize

When the DataGrid changes it�s Size, the actual columns should resize with the DataGrid. This is accomplished by listening for the Resize event from the DataGrid. When the grid resizes, we will calculate the total column width and verify it�s difference to the ClientSize of the DataGrid.

This is where the main problem comes in. Calculating the total column width is possible, but you may have noticed the second arrow (B) on the image above, it points to the before-first column, a column which you cannot access in any way that I am aware of, a column that has width! This will make it next to impossible to calculate the entire table width correctly. We can make an educated guess, but screen resolutions will make this a bit hairy. An educated guess it is, and it is called OFFSET_GRID. I have actually tested this on my monitor with different resolutions, for me the value is always the same. For you? I don�t know, probably not!

First a method for calculating the total grid width:

private int GetGridColumnWidth()
{
    // No columns, return error

    if(GridColumnStyles.Count == 0)
        return -1;
    // Easy 1

    int width = 0;
    foreach(DataGridColumnStyle columnStyle in GridColumnStyles)
    {
        width += columnStyle.Width;
    }

    return width + OFFSET_GRID;
}

Next the method that handles resizing events:

public void OnDataGridResize(object sender, EventArgs e)
{
    // Parent?

    if(DataGrid != null)
    {
        // Get column width

        int columnWidth;
        if( (columnWidth = GetGridColumnWidth()) != -1)
        {
            // Get the client width

            int clientWidth = DataGrid.ClientSize.Width;
            // Are there columns? redundant check

            if(GridColumnStyles.Count > 0)
            {
                // whats the newWidth

                int newWidth = 
                    GridColumnStyles [GridColumnStyles.Count - 1].Width +
                    clientWidth - columnWidth;
                // is the new width valid?

                if(newWidth > PreferredColumnWidth)
                    GridColumnStyles[GridColumnStyles.Count - 1].Width = 
                                                                   newWidth;
                else
                    GridColumnStyles[GridColumnStyles.Count - 1].Width = 
                                                        PreferredColumnWidth;
            }
        }
        // Redraw

        DataGrid.Invalidate(true);
    }
}

Call it

Now we are almost at the end of this article, finally I will show how to hook up this class into a DataGrid, just a few lines of code.

DataGrid gridView = new DataGrid();
AutoResizeDataGridTableStyle style = new AutoResizeDataGridTableStyle();
gridView.DataSourceChanged += new EventHandler(style.OnDataSourceChanged);
gridView.Resize += new EventHandler(style.OnDataGridResize);
style.MappingName = "MyTable";
gridView.TableStyles.Add(style);

Conclusion

After using C# for building WinForms applications, I�m afraid to say I liked Java better. The classes I have used lack the degree of customizability that I would like to see. Workarounds are available, but as the OFFSET_GRID item shows, it�s not always elegant.

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