George Shepherd's Windows Forms FAQ
Questions and answers in this FAQ have been collected from newsgroup posts, various mailing lists and the employees of Syncfusion.

5. Windows Forms Datagrid

WinForms FAQ Home
   5.1 How can I programatically set the rowheight of a row in my DataGrid?
   5.2 How can I autosize the rowheights in my datagrid?
   5.3 How do I prevent sorting a single column in my DataGrid?
   5.4 How do I programmatically select a row in DataGrid?
   5.5 How can I translate a point to a cell in the datagrid?
   5.6 I lost the settings in my DataGrid set during design-time?
   5.7 How do I prevent a click into the grid highlighting a cell?
   5.8 How do I prevent the datagrid from displaying its append row (the row at the end with an asterisk)?
   5.9 How can I catch a double-click into a column header cell?
   5.10 How can I select the entire row when the user clicks on a cell in the row?
   5.11 How can I get text from a column header in a MouseUp event?
   5.12 How do I hide a column?
   5.13 How do I color a individual cell depending upon its value or some external method?
   5.14 How can I put a checkbox in a column of my DataGrid?
   5.15 How can I restrict the keystrokes that will be accepted in a column of my datagrid?
   5.16 How do I make a field auto increment as new rows are added to my datagrid?
   5.17 How can I prevent a particular cell from being editable?
   5.18 How do I move columns in a datagrid?
   5.19 How can I do cell by cell validation in a datagrid?
   5.20 How do I programmatically determine the selected rows in a datagrid?
   5.21 How can I bind the datagrid to a datasource without using any wizards?
   5.22 How can I bind two datagrids in a Master-Detail relationship?
   5.23 How do I get the row or column that has been clicked on?
   5.24 How do I add an unbound column to my bound datagrid?
   5.25 How do I get the row and column of the current cell in my datagrid?
   5.26 How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter?
   5.27 How do I set the width of a column in my DataGrid?
   5.28 How can I implement OLE Drag & Drop between a DataGrid and another OLE DnD object that supports the Text format?
   5.29 How can I tell if the current row has changed and whether I am on the AddNew row or not?
   5.30 How do I hide the gridlines or set them to a particular color?
   5.31 How can I get the selected text in an active gridcell?
   5.32 How do I determine whether a checkbox in my datagrid is checked or not?
   5.33 How can I move rows by dragging the row header cell?
   5.34 How can I control the cursor over my DataGrid?
   5.35 After scrolling with the mouse wheel on a selected row in a DataGrid I cannot get it back into view. Is there a work around?
   5.36 How can I make the DataGrid column be blank and not display (null) as the default value?
   5.37 How can I add a DateTimePicker column style to the DataGrid?
   5.38 How can I put a combobox in a column of a datagrid?
   5.39 I want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members?
   5.40 How can I have a column of icons in my datagrid?
   5.41 How can I make my DataGrid support a single select mode, and not the default multiselect mode?
   5.42 How can I get celltips or tooltips to vary from cell to cell in my DataGrid?
   5.43 How can I get notification of the changing of a value in a column of comboboxes within my datagrid?
   5.44 How can I make the datagrid have no currentcell?
   5.45 How can I make my grid never have an active edit cell and always select whole rows (as in a browser-type grid)?
   5.46 I have hidden (column width = 0) columns on the right side of my datagrid, but tabbing does not work properly. How can I get tabbing to work?
   5.47 How can I get the number of rows in my DataGrid?
   5.48 How do I format a date column in a datagrid?
   5.49 How can I change the width of the row headers or hide them?
   5.50 How do I catch a doubleclick in my datagrid?
   5.51 How can I make my last column wide enough to exactly occupy all the client area of the datagrid?
   5.52 How can I prevent my user from sizing columns in my datagrid?
   5.53 How can I catch the bool values changing in a DataGridBoolColumn?
   5.54 When I click on a row header, the row is selected and no cell is active. How can I do this programmatically?
   5.55 How can I force the vertical scrollbar in my DataGrid to always be visible?
   5.56 How can I autosize a column in my datagrid?
   5.57 How can I get rid of the error icon that appears when there is an editing error?
   5.58 How do I find the top-left visible cell in a datagrid?
   5.59 I want to display negative values with a CR suffix, instead of a minus sign. How can I perform custom formatting on the cells in my datagrid?
   5.60 I want to do sort of a database join of two tables. How can I use data from two DataTables in a single DataGrid?
   5.61 How do I display a column of buttons such as pushbuttons or combobox buttons?
   5.62 How can I put up a confirmation question when the user tries to delete a row in the datagrid by clicking on the row header and pressing the Delete key?
   5.63 How can I enable column selections in my datagrid?
   5.64 How do I programmatically scroll the datagrid to a particular row?
   5.65 How can I place text in the rowheader column of my datagrid?
   5.66 How do I set default values for new rows in my datagrid?
   5.67 How do I iterate through all the rows and columns in my datagrid?
   5.68 How can I specially color only the currentcell of my readonly datagrid?
   5.69 How can I make the Enter Key behave like the Tab Key and move to the next cell?
   5.70 How do I use the DataColumn.Expression property to add a computed/combined column to my datagrid?
   5.71 How can I change the font used in a grid cell on a cell by cell or row by row basis?
   5.72 How can I use a mouse to select cell ranges in my datagrid?
   5.73 How do I determine the DataGridTableStyle MappingName that should used for a DataGrid to make sure the grid uses my tablestyle?
   5.74 I have a derived DataGridColumnStyle. From within my Paint override, how can I get at other values in the DataGrid?
   5.75 How do I retrieve the current row from a DataTable bound to a DataGrid after the grid has been sorted?
   5.76 How can I catch when the user clicks off the grid, say to close the form?
   5.77 How can I get a CheckBox column in a DataGrid to react to the first click?
   5.78 How can I use events to restrict key input to grid cells?
   5.79 How can I format columns in my DataGrid without explicitly adding DataGridColumnStyles?
   5.80 How can I auto-adjust keyboard input? For example, make typing 12312002 be taken as a valid date, 12/31/2002.
   5.81 Can I display the rows in my datagrid in a free-form layout using textboxes on a panel?
   5.82 How can I tell whether a scrollbar is visible in my DataGrid is visible?
   5.83 How do I autosize the columns in my DataGrid so they always fill the the grid's client area?
   5.84 How can I prevent all the cells in my DataGrid from being edited without deriving GridColumnStyle?
   5.85 How can I prevent the plus-minus icon that appears next to the row header when I have a datagrid displayed bound to a datasource that has a relation defined?
   5.86 How can I display master-details-details in three separate grids?
   5.87 In my datagrid, if I press tab to enter a column using a derived columnstyle, the column does not receive focus. Why?
   5.88 How can I detect when a cell starts being edited, not when it becomes current?
   5.89 How can I have a column of bitmaps in a DataGrid?
   5.90 How can I add my custom columnstyles to the designer so I can use them in my DataGrid at design time?
   5.91 How can I programatically add and remove columns in my DataGrid without modifying the DataTable datasource?



5.1 How can I programatically set the rowheight of a row in my DataGrid?


You can do this by starting a new thread and executing Application.Run for the status dialog form when the background thread starts running. To communicate changes in percentage use BeginInvoke to executes a specific delegate asynchronously on the thread that the form was created on.

Download the ProgressThread progressthread.zip sample for a complete implementation of a BackgroundThreadStatusDialog class that shows a status dialog in a separate thread.

In order to use BackgroundThreadStatusDialog from your code you have to update the Progress inside your loop and check the IsCanceled state to detect if the user pressed the Cancel button.


          private void button1_Click(object sender, System.EventArgs e)
          {
               BackgroundThreadStatusDialog statusDialog = new BackgroundThreadStatusDialog();
               try
               {
                    for (int n = 0; n < 1000; n++)
                    {
                         statusDialog.Percent = n/10;
                         int ticks = System.Environment.TickCount;
                         while (System.Environment.TickCount - ticks < 10)
                              ;
                         if (statusDialog.IsCanceled)
                              return;
                    }
                    statusDialog.Close();
                    MessageBox.Show(statusDialog.IsCanceled ? "Canceled" : "Success");
               }
               finally
               {
                    statusDialog.Close();
               }
          }



5.2 How can I autosize the rowheights in my datagrid?


Here is a solution suggested by Matthew Benedict. It uses reflection to access the protected DataGridRows collection so he can set the row height.

public void AutoSizeGrid()
{
// DataGrid should be bound to a DataTable for this part to
// work.
int numRows = ((DataTable)gridTasks.DataSource).Rows.Count;
Graphics g = Graphics.FromHwnd(gridTasks.Handle);
StringFormat sf = new StringFormat(StringFormat.GenericTypographic);
SizeF size;

// Since DataGridRows[] is not exposed directly by the DataGrid
// we use reflection to hack internally to it.. There is actually
// a method get_DataGridRows that returns the collection of rows
// that is what we are doing here, and casting it to a System.Array
MethodInfo mi = gridTasks.GetType().GetMethod("get_DataGridRows",
BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase | BindingFlags.Instance
| BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);

System.Array dgra = (System.Array)mi.Invoke(gridTasks,null);

// Convert this to an ArrayList, little bit easier to deal with
// that way, plus we can strip out the newrow row.
ArrayList DataGridRows = new ArrayList();
foreach (object dgrr in dgra)
{
if (dgrr.ToString().EndsWith("DataGridRelationshipRow")==true)
DataGridRows.Add(dgrr);
}

// Now loop through all the rows in the grid
for (int i = 0; i < numRows; ++i)
{
// Here we are telling it that the column width is set to
// 400.. so size will contain the Height it needs to be.
size = g.MeasureString(gridTasks[i,1].ToString(),gridTasks.Font,400,sf);
int h = Convert.ToInt32(size.Height);
// Little extra cellpadding space
h = h + 8;

// Now we pick that row out of the DataGridRows[] Array
// that we have and set it's Height property to what we
// think it should be.
PropertyInfo pi = DataGridRows[i].GetType().GetProperty("Height");
pi.SetValue(DataGridRows[i],h,null);

// I have read here that after you set the Height in this manner that you should
// Call the DataGrid Invalidate() method, but I haven't seen any prob with not calling it..

}

g.Dispose();
}


5.3 How do I prevent sorting a single column in my DataGrid?


You can implement the IMessageFilter interface in your main form. This amounts to adding an override for PreFilterMessage, and looking for the particular message you need to catch. Here are code snippets that catch an escape key on a keydown. You can download a sample project(C#, VB). In the sample, there are two forms, with several controls. You'll notice that no matter what form or control has input focus, the escape key is caught in the PreFilterMessage override.

[C#]
public class MyMainForm : System.Windows.Forms.Form, IMessageFilter
{
     const int WM_KEYDOWN = 0x100;
     const int WM_KEYUP = 0x101;

     public bool PreFilterMessage(ref Message m)
     {
          Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
          if(m.Msg == WM_KEYDOWN     && keyCode == Keys.Escape)
          {
               Console.WriteLine("Ignoring Escape...");
               return true;
          }
          return false;
     }
     ....
     ....
     ....
     private void MyMainForm_Load(object sender, System.EventArgs e)
     {
          Application.AddMessageFilter(this);
     }
}

[VB.NET]
Public Class MyMainForm
     Inherits System.Windows.Forms.Form
     Implements IMessageFilter

     Private WM_KEYDOWN As Integer = &H100
     Private WM_KEYUP As Integer = &H101

     Public Function PreFilterMessage(ByRef m As Message) As Boolean
          Dim keyCode As Keys = CType(CInt(m.WParam), Keys) And Keys.KeyCode
          If m.Msg = WM_KEYDOWN And keyCode = Keys.Escape Then
               Console.WriteLine("Ignoring Escape...")
               Return True
          End If
          Return False
     End Function 'PreFilterMessage
....
....
....
     Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs)
          Application.AddMessageFilter(Me)
     End Sub 'MyMainForm_Load
End Class 'MyMainForm


5.4 How do I programmatically select a row in DataGrid?


We have a small sample that shows how to do this. Download PropTab.zip. After you download and unzip the sample, build the project. Both the control assembly and a small driver assembly get built. After that add the control to your toolbox using 'Customise toolbox...'. Then drag and drop an instance of the added control onto the form in the driver winforms sub-project. When you select this control the properties window should have a tab with a bitmap 'T' as shown below.

These are the steps involved. Before following these steps please download and take a look at the sample. That will greatly help when following these steps.

Control related steps

Assume that you have a control that has two sets of properties, one set that you wish to have displayed in the main property tab and another set that you wish to have displayed in the second tab.

  • Mark those properties that you wish to display in the first tab with the BrowsableAttribute to true.
  • Mark those properties that you wish to display in the second tab with the BrowsableAttribute set to false. Create another attribute. Name it anything you want and give it a single boolean property. Initialize this property to true.

Other steps

  • Derive a class from System.Windows.Forms.PropertyGridInternal.PropertiesTab. You have to override a few methods. The most important of these is GetProperties. We override GetProperties as shown below to use our CustomAttribute as the filtering attribute instead of the BrowsableAttribute that the PropertyGrid uses by default.


public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object component, Attribute[] attrs)
          {
               return TypeDescriptor.GetProperties(component, new Attribute[]
                    {new BrowsableAttribute(false), new CustomTabDisplayAttribute(true)});
          }


  • Create a embedded resource bitmap with the same name as the derived tab's type. This bitmap had to be 16x16.

    A brief note of explanation on the sample. The sample shows a user control that displays its own tab for some properties. These properties are marked with the filtering attribute, CustomTabDisplayAttribute. The rest of the properties are just displayed in the normal property tab and need no special attention.

    To get a deeper understanding of how this works, please refer to these FAQs on TypeConverters and TypeDescriptors.


  • 5.5 How can I translate a point to a cell in the datagrid?


    PE refers to portable executable. The file format used for executable programs and for files to be linked together to form executable programs. It includes both metadata information that describes types and members referenced in your program, and the MSIL that is the code base for the program. Source: .NET Framework SDK Documentation


    5.6 I lost the settings in my DataGrid set during design-time?


    This is possible if you assign a custom DataGridTableStyle to the DataGrid. The properties in the DataGridTableStyle will then replace certain properties in the DataGrid. Take a look at DataGrid class reference for a list of these properties.


    5.7 How do I prevent a click into the grid highlighting a cell?


    Here is a technique for binding an arraylist of objects where the objects contain public property that can appear as columns in the datagrid. In this example, the object contains 2 public doubles, one named value and the other named sqrt. To bind this arraylist to a datagrid, add a custom tablestyle that has a MappingName of "ArrayList", and then use the property names as the MappingName for each column. Below are some code snippets. You can download a working project that also has code to delete rows and add new rows to the bound arraylist.

    private void Form1_Load(object sender, System.EventArgs e)
              {
                   CreateArrayList();
                   BindArrayListToGrid();
              }

              private void BindArrayListToGrid()
              {
                   dataGrid1.DataSource = arrayList1;

                   //create a custom tablestyle and add two columnstyles
                   DataGridTableStyle ts = new DataGridTableStyle();
                   ts.MappingName = "ArrayList";

                   int colwidth = (dataGrid1.ClientSize.Width - ts.RowHeaderWidth - SystemInformation.VerticalScrollBarWidth - 5) / 2;

                   //create a column for the value property
                   DataGridTextBoxColumn cs = new DataGridTextBoxColumn();
                   cs.MappingName = "value"; //public property name
                   cs.HeaderText = "Random Number";
                   cs.Format = "f4";
                   cs.Width = colwidth;
                   ts.GridColumnStyles.Add(cs);

                   //create a column for the sqrt property
                   cs = new DataGridTextBoxColumn();
                   cs.MappingName = "sqrt"; //public property name
                   cs.HeaderText = "Square Root";
                   cs.Format = "f4";
                   cs.Width = colwidth;
                   ts.GridColumnStyles.Add(cs);

                   dataGrid1.TableStyles.Clear();
                   dataGrid1.TableStyles.Add(ts);
              }

              private void CreateArrayList()
              {
                   arrayList1 = new ArrayList();

                   //add some items
                   Random r = new Random();
                   for (int i = 0; i < 20; ++i)
                        arrayList1.Add(new RandomNumber(r.NextDouble()));

              }

              //create a struct or class that defines what you want in each row
              //the different columns in the row must be public properties
              public struct RandomNumber
              {
                   private double number;

                   public RandomNumber(double d)
                   {
                        number = d;
                   }

                   public double value
                   {
                        get{ return number; }
                        set{ number = value;}
                   }

                   public double sqrt
                   {
                        get {return Math.Sqrt(this.value);}
                   }
              }


    5.8 How do I prevent the datagrid from displaying its append row (the row at the end with an asterisk)?


    The DataGrid class does not have a property that controls whether a new row can be added. But the DataView class does have such a property (along with some others such as AllowEdit and AllowDelete). Here is code that will turn off the append row by getting at the dataview associated with the datagrid.

         string connString = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
         string sqlString = "SELECT * FROM customers";

         // Connection object
         OleDbConnection connection = new OleDbConnection(connString);

         // Create data adapter object
         OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sqlString, connection);

         // Create a dataset object and fill with data using data adapter's Fill method
         DataSet dataSet = new DataSet();
         dataAdapter.Fill(dataSet, "customers");
                   
         // Attach dataset's DefaultView to the datagrid control
         dataGrid1.DataSource = dataSet.Tables["customers"];

         //no adding of new rows thru dataview...
         CurrencyManager cm = (CurrencyManager)this.BindingContext[dataGrid1.DataSource, dataGrid1.DataMember];     
         ((DataView)cm.List).AllowNew = false;

    If your datagrid contains links, then Matthew Miller suggest adding Navigate handler such as the one below to disallow the AddNew.

    private void DataGrid1_Navigate(object sender, System.Windows.Forms.NavigateEventArgs ne)
    {
         if(ne.Forward)
         {
              CurrencyManager cm = (CurrencyManager)BindingContext[DataGrid1.DataSource,DataGrid1.DataMember];
              DataView dv = (DataView) cm.List;
              dv.AllowNew = false;
         }
    }


    5.9 How can I catch a double-click into a column header cell?


    You can use the DataGrid's double-click event and its HitTest method to catch a double click on a header.

         private void dataGrid1_DoubleClick(object sender, System.EventArgs e)
         {
              System.Drawing.Point pt = dataGrid1.PointToClient(Cursor.Position);

              DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);

              if(hti.Type == DataGrid.HitTestType.ColumnHeader)
              {
                   MessageBox.Show("double clicked clicked column header " + hti.Column.ToString());
              }
         }


    5.10 How can I select the entire row when the user clicks on a cell in the row?


    Call the DataGrid.Select method from within its mouseup event.

    [C#]
    private void dataGrid1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
    {
         System.Drawing.Point pt = new Point(e.X, e.Y);
         DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);
         if(hti.Type == DataGrid.HitTestType.Cell)
         {
              dataGrid1.CurrentCell = new DataGridCell(hti.Row, hti.Column);
              dataGrid1.Select(hti.Row);
         }
    }

    [VB/NET]
    Private Sub dataGrid1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dataGrid1.MouseUp
         Dim pt = New Point(e.X, e.Y)
         Dim hti As DataGrid.HitTestInfo = dataGrid1.HitTest(pt)
         If hti.Type = DataGrid.HitTestType.Cell Then
              dataGrid1.CurrentCell = New DataGridCell(hti.Row, hti.Column)
              dataGrid1.Select(hti.Row)
         End If
    End Sub


    5.11 How can I get text from a column header in a MouseUp event?


         private void dataGrid1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
         {
              System.Drawing.Point pt = new Point(e.X, e.Y);
              DataGrid.HitTestInfo hti = dataGrid1.HitTest(pt);
              if(hti.Type == DataGrid.HitTestType.Cell)
              {
                   MessageBox.Show(dataGrid1[hti.Row, hti.Column].ToString());
              }
              else if(hti.Type == DataGrid.HitTestType.ColumnHeader)
              {
                   MessageBox.Show(((DataView) DataGrid1.DataSource).Table.Columns[hti.Column].ToString());
              }
         }


    5.12 How do I hide a column?


    There are several ways to hide a column:

    1) You can use your DataSet's ColumnMapping property to hide a column.

         // Creating connection and command sting
         string conStr = @"Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb";
         string sqlStr = "SELECT * FROM Employees";
         // Create connection object
         OleDbConnection conn = new OleDbConnection(conStr);
         // Create data adapter object
         OleDbDataAdapter da = new OleDbDataAdapter(sqlStr,conn);

         // Create a dataset object and fill with data using data adapter's Fill method
         DataSet ds = new DataSet();
         da.Fill(ds, "Employees");

         // Hide the column and attach dataset's DefaultView to the datagrid control
         ds.Tables["Employees"].Columns["LastName"].ColumnMapping = MappingType.Hidden;
         dataGrid1.DataSource = ds.Tables["Employees"];


    2) Another way to hide a column is to set its width to zero. Check out the FAQ How do I set the width of a column in my DataGrid?.

    3) Another way to hide a column is to create a custom table style, and as you add columnstyles to your tablestyle, omit the column you want hidden. Check out the FAQ How do I add an unbound column in my bound DataGrid? to see how to create a custom table style.


    5.13 How do I color a individual cell depending upon its value or some external method?


    We give three different methods for doing this.

  • The first one overrides Paint in a derived columnstyle and sets the backcolor there.
  • The second uses a delegate to set the color in the Paint override.
  • The third method adds an event to the derived column style to allow you to set the color in an event handler.


  • Method 1
    You can do this by deriving from DataGridTextBoxColumn and overriding the Paint method to conditionally set the backColor and foreColor. The sample code below colors any cell that starts with a letter higher than 'F'. You can download a project (C#, VB) using this class.

    [C#]
         public class DataGridColoredTextBoxColumn : DataGridTextBoxColumn
         {
              protected override void Paint(System.Drawing.Graphics g,
                   System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager