Autosize a DataGridView to Fit

This article describes how you can resize a dbgrid to always fit the form after a resize so that you never have a horizontal scrollbar.

Description

What the code does is first recalculate all columns’ width (if you have visible columns). But, at the end, you could have rounding errors and still have a scrollar if the last column is 1 or more pixels too wide. So, at the end you calculate the exact width needed for the last column.

Let me describe some importand code fragments.

int fixedWidth = SystemInformation.VerticalScrollBarWidth +
   dataGrid.RowHeadersWidth + 2;
int mul = 100 * (dataGrid.Width - fixedWidth) /
   (prevWidth - fixedWidth);

First, check the fixed width of the datagrid. This is the width of the scrollbar and the width of the row headers. You include the row headers because resizing these is very ugly. Now, what about this multiplying by 100? You are going to multiply/divide the width of the columms with a number containing a decimal point (for example, if you make the form 50 percent, you will multiply by 0.5). But, working with float numbers is very slow and not needed with a little imagination). So, you take an integer and multiply by 100 to get 2 decimal fractions.

The correction of adding 2 pixels to the fixedWidth is important. This is dependent on the BorderStyle of the grid. You need the 2 pixel if it is set to FixedStyle. You don’t need it when it is set to None, and you have to increase it if set to Fixed3D, of course.

Then, you step trough all visible columns to calculate the new width. Of course, you take into account that there is a MinimumWidth property.

columnWidth = (dataGrid.Columns[i].Width * mul + 50)
   / 100;
dataGrid.Columns[i].Width =
   Math.Max(columnWidth,
   dataGrid.Columns[i].MinimumWidth);

What about thie + 50? Well, remember that we emulate a floating point with integer. So, by adding 50 before dividing by 100, you round it. If you do not, we truncate, and with every resize each column will be a little smaller and smaller.

The last thing to do is check the exact width of the last column. This is nececary because you could have little rounding errors, or be limited by the MinimumWidth of some columns. Then, you end up with either a scrollbar or a blank space after the last column, both of which is very ugly.

Complete Code

public void ResizeGrid(DataGridView dataGrid, ref int prevWidth)
{
   if (prevWidth == 0)
      prevWidth = dataGrid.Width;
   if (prevWidth == dataGrid.Width)
      return;

   int fixedWidth = SystemInformation.VerticalScrollBarWidth +
      dataGrid.RowHeadersWidth + 2;
   int mul = 100 * (dataGrid.Width - fixedWidth) /
      (prevWidth - fixedWidth);
   int columnWidth;
   int total = 0;
   DataGridViewColumn lastVisibleCol = null;

   for (int i = 0; i < dataGrid.ColumnCount; i++)
      if (dataGrid.Columns[i].Visible) {
         columnWidth = (dataGrid.Columns[i].Width * mul + 50) / 100;
         dataGrid.Columns[i].Width =
            Math.Max(columnWidth, dataGrid.Columns[i].MinimumWidth);
         total += dataGrid.Columns[i].Width;
         lastVisibleCol = dataGrid.Columns[i];
      }
   if (lastVisibleCol == null)
      return;
   columnWidth = dataGrid.Width - total +
      lastVisibleCol.Width - fixedWidth;
   lastVisibleCol.Width =
      Math.Max(columnWidth, lastVisibleCol.MinimumWidth);
   prevWidth = dataGrid.Width;
}

Test Application

This is how to use it. Note that you have two variables in your form, to remember the WindowState and the last width.

You do not recalculate in the Resize event because there will be so many recalculations (especially when the user resizes very slowly) that you will get out of proportion due to rounding errors. So, you use ResizeEnd to calculate one time after resizing. But, you also need to recalculate when the user maximizes or restores the form. That you do in the Resize event.

This is the complete code:

namespace ResizeGridDemo
{
   public partial class main: Form
   {
      int prevWidth;
      FormWindowState prevWindowState;

      public main()
      {
         InitializeComponent();
         prevWidth = Width;
         prevWindowState = WindowState;
      }

      private void main_ResizeEnd(object sender, EventArgs e)
      {
         ResizeGrid(dataGrid, ref prevWidth);
      }

      private void main_Resize(object sender, EventArgs e)
      {
         if (WindowState != prevWindowState && WindowState !=
             FormWindowState.Minimized) {
            prevWindowState = WindowState;
            ResizeGrid(dataGrid, ref prevWidth);
         }
      }
   }
}

Summary

Rather that put all this code in a form, it could be in a subclass of DataGridView, or even in a new component derived from it.

Note: I use VS beta 2. Things can be different in the .NET 1.1 version.

If you use this code in a project, you only need it one time; for example, in a static class. Each form must have the prevSize variable, and if the form can be maximized, it also needs the prevWindowState variable.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read