WinForms FAQ - Datagrid

How do I autosize the columns in my DataGrid so they always fill the the grid’s client area

If you add a DataGridTableStyle to your Datagrid, then you can use the ColWidth property of the GridColumnStyles to set the width of each column. To dynamically set these widths as the grid is resized, you can handle the SizeChanged event of the the DataGrid. In your handler, you can compute the width of each column by dividing the client width minus the width of the row header column by the number of columns. Now there are a couple of technical points. You have to adjust for a possible vertical scrollbar. And, you have to adjust things for possible integer rounding in the calculations. To handle this last problem, the attached samples (both VB and C#) apply the single computed width to all but the last column. And this last column is just given all the space left. This means the last column may differ in width from the other columns by a couple of pixels.

Permalink
How can I control the cursor over my DataGrid

One way you can do this is to derive the DataGrid and override its WndProc method to handle the WM_SETCURSOR method yourself. In your override, you can do hit testing to decide when you want to set the cursor, or when you want to call the base class and let it set the cursor. Here are sample projects (VB and C#) showing the technique.

Public Class MyDataGrid
  Inherits DataGrid
  Private Const WM_SETCURSOR As Integer = 32

  Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg <> WM_SETCURSOR Then
      MyBase.WndProc(m)
    Else
      ’see if you want the cursor - in col 1, rows 2, 3, 4
      Dim pt As Point = Me.PointToClient(Control.MousePosition)
      Dim hti As DataGrid.HitTestInfo = Me.HitTest(pt.X, pt.Y)
      If hti.Column = 1 AndAlso hti.Row > 1 AndAlso hti.Row < 5 Then
          Cursor.Current = Cursors.Hand
        ’if not, call the baseclass
       Else
        MyBase.WndProc(m)
      End If
    End If
  End Sub ’WndProc

  Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    If Cursor.Current.Equals(Cursors.Hand) Then
      MessageBox.Show(''My MouseDown'')
    Else
      MyBase.OnClick(e)
    End If
  End Sub ’OnMouseDown
End Class ’MyDataGrid
Permalink
How can I display master-details-details in three separate grids

Here is a sample (both VB and C#) that illustrates how to have a parent table which has a related child table, which also has a related grandchild table.

Below are some code snippets. The trick is to always make the main parent table be the DataSource for all the grid, and then set the DataMember to be the relation name where teh relation starts at the parent table. This means the DisplayMember for the Child table is ”ParentToChild”, the name of that relation. And, the DisplayMember for the grandchild grid is ”ParentToChild.ChildToGrandChild” which defines the relation starting at the parent grid through the child grid.

Dim dSet As New DataSet()

        ’get the tables
        Dim parentTable As DataTable = GetParentTable()
        Dim childTable As DataTable = GetChildTable()
        Dim grandChildTable As DataTable = GetGrandChildTable()
        dSet.Tables.AddRange(New DataTable() {parentTable, childTable, grandChildTable})

        ’setup the relations
        Dim parentColumn As DataColumn = parentTable.Columns(''parentID'')
        Dim childColumn As DataColumn = childTable.Columns(''ParentID'')
        dSet.Relations.Add(''ParentToChild'', parentColumn, childColumn)

        parentColumn = childTable.Columns(''childID'')
        childColumn = grandChildTable.Columns(''ChildID'')
        dSet.Relations.Add(''ChildToGrandChild'', parentColumn, childColumn)

        ’set the grids
        Me.dataGrid1.DataSource = parentTable

        Me.dataGrid2.DataSource = parentTable 
        Me.dataGrid2.DataMember = ''ParentToChild''

        Me.dataGrid3.DataSource = parentTable 
        Me.dataGrid3.DataMember = ''ParentToChild.ChildToGrandChild''

        Me.dataGrid1.AllowNavigation = False
        Me.dataGrid2.AllowNavigation = False
        Me.dataGrid3.AllowNavigation = False
Permalink
How can I use a mouse to select cell ranges in my datagrid

The Windows Forms DataGrid does not support the selection of a range of cells other than a group of rows. To select a row, you can click on its row header. But if you want to select a rectangular group of cells, you cannot.

To add this support, you can catch the mousedown on a cell, and in mousemove, track whether the mouse is dragged with the button down to other cells. If so, draw a selection rectangle over this group of cells. The attached samples (C#, VB) show how this might be done. The drawing of the selection rectangle is done in an OnPaint override. The samples also try to handle autoscrolling as you hit a grid border so the selection process can continue for cells that are not currently visible. To make the selection rectangle look nicer, the code also hides the current cell when it draws a selection rectangle.

The samples have a derived DataGrid that has a public member, SelectedRange, that holds the TopLeft and BottomRight coordinates of the selected range. The derived grid also has a public event, SelectionChanging, that fires prior to the selection changing as you drag your mouse. The event passes both the old selection and the new selection, and allows you to cancel the action if you choose.

Permalink
Can I display the rows in my datagrid in a free-form layout using textboxes on a panel

Here is a VB and C# sample showing how you might do this.

The idea is to add textboxes and labels to a panel with whatever layout you want to use.

Then use DataBindings.Add calls to bind the textboxes to columns in your grid (actually in your grid’s datasource). Then, since the grid and the textboxes share the same BindingContext, you can move the BindingManagerBase.Position property to scroll through the rows in your grid, and the textboxes will remain in sync. You can hide the grid just to show the textboxes if you do not want the grid to have a presence. Also, any edits in the textboxes will appear in the DataGrid and vice versa.

Permalink
How can I have a column of bitmaps in a DataGrid

You can derive a custom columnstyle and override its Paint method to draw the image. Here are both C# and VB.Net samples.

The Paint override offers three ways to draw the bitmap in the cell; size it to fit the cell bounds, size it proportionally to fit the cell bounds, and draw it with it’s original size (clipping the bitmap to fit in the cell bounds). In the sample, the DataGrid is anchored on all four sides, so as you size the form, the DataGrid sizes as well. The dataGrid1_Resized event is handled to dynamically change the PreferredRowHeight and PreferredColumnWidth so the cells occupy all of the datagrid’s client area. So, as you size the form, the grid cells size also so you can easily see the bitmaps.

Permalink
How can I detect when a cell starts being edited, not when it becomes current

You can use the CurrentCellChanged event to detect when the currentcell changes position. But this event will not allow you to catch the start of a cell being edited.

One way you can do this is to catch the TextChanged event in the embedded TextBox within the cell. If the text changes, then it might be the beginning of a cell edit provided the text in the TextBox differs from the stored value from the DataSource. The reason you need to check for a different value between the grid DataSource and the TextBox contents is that the TextChanged event is fired initially when the TextBox is initialized when the cell becomes current and moves the value from the grid ataSource to the TextBox. You also have to ignore subsequent hits of TextChanged as the same cell continues to be edited. Here is both a VB and C# sample that implements this strategy to flagged current cell start editing.

Permalink
How can I add my custom columnstyles to the designer so I can use them in my DataGrid at design time

To use custom columnstyles in the designer, you need to do three things.

1) Derive a CustomColumnStyle to implement the functionality you want.
2) Derive a DataGridTableStyle class and add a new GridColumnStyles property that uses this derived CollectionEditor. This GridColumnStyle hides the baseclass member.
3) Derive a DataGrid and add a new TableStyles collection that uses your derived tablestyle.

Both steps 2 and 3 will require you to derive a CollectionEditor and override CreateNewItemTypes to use the derived classes from each step in the designer.

Here is a sample project showing how you might do these things.

Permalink
How can I programatically add and remove columns in my DataGrid without modifying the DataTable datasource

You can control the columns displayed in the DataGrid through the DataGrid.TableStyle[0].GridColumnStyles collection. To do so, create a DataGridTableStyle and set its MappingName property to point to the name of your DataTable which is the DataSource for the DataGrid. Next, add this DataGridTableStyle to the DataGrid.TableStyles property. Finally, set the DataGrid.DataSource property to the DataTable. Doing things in this order, guarantees that the DataGridTableStyle.GridColumnStyles collection will be fully populated showing all the DataTable columns in the the DataGrid.

Then to add and remove columns from the DataGrid, you only have to add and remove DataGridColumnStyle objects from this DataGrid.TableStyle[0].GridColumnStyles collection. Removing them is straight-forward through a Remove method call. But inserting them requires more work as there is no InsertAt method defined for this collection. To handle this problem, you can create a new array of DataGridColumnStyles, and populate this array in the necessary order to reflect the DataGrid with an inserted column. Then you can clear the old collection, and create a new collection with this new array. You really are not creating all new DataGridColumnStyle objects, but are simply reordering the existing ones in a new collection.

Here is a sample project containing both C# and VB.NET code showing how you might do this.

Permalink
How can I change the font used in a grid cell on a cell by cell or row by row basis

One way to do this is to use a derived columnstyle, override the Paint method and do the text drawing yourself, using whatever font or colors you like. If you add an event to your derived column style that is fired immediately before the text is drawn, and use the event args to get the font and color information, you can let the event handler completely determine the look of any cell.

The attached samples (C#, VB) use this technique to create a grid that looks like the grid in the picture. Both color and font varies on a cell basis or row basis in this picture.

Permalink
How do I catch a doubleclick in my datagrid

The problem is that the first click of a double click may be caught by the datagrid (and used to activate the cell) while the second click goes to the TextBox for the columnstyle object. This means the TextBox thinks this is a singleclick, and does not fire its doubleclick event. One solution is to mark the time of the click caught by the datagrid. Then look at this time in the TextBox’s mousedown handler to see if in fact the single click being looked at by the TextBox is part of a double click. You can download a sample (C#, VB) that illustrates how this might be done.

Permalink
How can I auto-adjust keyboard input? For example, make typing 12312002 be taken as a valid date, 12/31/2002.

One possible solution is that as you move off the cell, you get the typed value, and modify it before it is passed onto the DataGrid for its standard processing.

One problem is that there are several ways to leave the cell, and you would want to handle each (depending upon your needs). You can leave the cell by clicking on another cell, by tabbing off the cell, by arrowing off the cell, and by pressing Enter on an edited cell.

Another problem is that you want to catch these actions early enough to make a change in the typed value that can be passed onto the DataGrid itself. To catch the typed entries before they are handed off to the DataGrid, you have to gain access to the DataGridTextBoxColumn’s embedded TextBox. This requires that your DataGrid either has explicitly had these columns styles added to it, or that you minimally add a DataGridTableStyle to your DataGrid so the Framework generates ColumnsStyle objects for you.

To solve these problems, you can derive the dataGrid and override OnMouseDown, OnProcessDialogKey, and OnProcessPreviewKey. The last override will handle both the arrowing off the cell and pressing Enter on an edited cell.

You can download a sample (C#, VB.NET) that implements this technique.

Permalink
How can I format columns in my DataGrid without explicitly adding DataGridColumnStyles

To format column output, you do not have to explicitly add DataGridColumns for each column provided you do add a DataGridTableStyle to your DataGrid prior to setting the DataSource property. When you set the DataSource property with a DataGrid that has a tablestyle with an empty columnstyle collection, the framework generates default columnstyle objects for each column in the datasource. You can then access these columnstyles directly and set properties in them such as Format, HeaderText and Width.

Download working samples here (VB.NET, C#).

Dim dataTableName As String = ''theTable''

 ’add a tablestyle to the grid so there will be custom columnstyles available
 ’    after the datasource has been set....
Dim ts As New DataGridTableStyle()
ts.MappingName = dataTableName

Me.dataGrid1.TableStyles.Add(ts)
Me.dataGrid1.DataSource = GetTheTable(dataTableName)

’now default customcolumnstyles have been created, so we can use them to set properties
Dim dgtbc As DataGridTextBoxColumn

’format the int
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(0)
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''n0''
End If

’format the double
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(1) ’
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''f3'' ’ 0r ''#.000''; 
End If

’format the double as currency
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(2) ’
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''c4''
End If

’format the date
dgtbc = dataGrid1.TableStyles(0).GridColumnStyles(3) ’
If Not (dgtbc Is Nothing) Then
  dgtbc.Format = ''d'' ’ or  ''g'' or ''u'' or whatever format you want to see
  dgtbc.Width = 100 ’size it
End If
Permalink
How can I autosize a column in my datagrid

One way to do this is to use MeasureString to compute the size of the text in each cell, and then take the maximum value. Below is a code snippet that does this. It assumes your datagrid is bound to a datatable. You can download a full working sample. (C#,VB).

public void AutoSizeCol(int col)
{
  float width = 0;
  int numRows = ((DataTable) dataGrid1.DataSource).Rows.Count;
    
  Graphics g = Graphics.FromHwnd(dataGrid1.Handle);
  StringFormat sf = new StringFormat(StringFormat.GenericTypographic);
  SizeF size;

  for(int i = 0; i < numRows; ++ i)
  {
    size = g.MeasureString(dataGrid1[i, col].ToString(), dataGrid1.Font, 500, sf);
    if(size.Width > width)
      width = size.Width;
  }

  g.Dispose();

  dataGrid1.TableStyles[''customers''].GridColumnStyles[col].Width = (int) width + 8; // 8 is for leading and trailing padding
}
Permalink
How can I make my last column wide enough to exactly occupy all the client area of the datagrid

If you have added a TableStyle for your grid, then the code below should set the right column width to be the empty space from a button click. If you need to dynamically do this in response to the user sizing other columns, then there may be more work. But if you only need to do it at the end of your Form_Load, then this code might be sufficient. It assumes your datasource is a datatable. You can download a sample project (C#, VB).

private void button1_Click(object sender, System.EventArgs e)
{
  int numCols = ((DataTable)(dataGrid1.DataSource)).Columns.Count;

  //the fudge -4 is for the grid borders
  int targetWidth = dataGrid1.ClientSize.Width - SystemInformation.VerticalScrollBarWidth - 4;

  int runningWidthUsed = this.dataGrid1.TableStyles[''customers''].RowHeaderWidth; 
      
  for(int i = 0; i < numCols - 1; ++i)
    runningWidthUsed += this.dataGrid1.TableStyles[''customers''].GridColumnStyles[i].Width;

  if(runningWidthUsed < targetWidth)
    this.dataGrid1.TableStyles[''customers''].GridColumnStyles[numCols - 1].Width = targetWidth - runningWidthUsed;
}
Permalink
How can I catch the bool values changing in a DataGridBoolColumn

There is no event fired when the boolean value changes. In the attached sample (C#, VB), a BoolValueChanged event has been added to a columnstyle derived from DataGridBoolColumn. Catching the changes requires some effort. The strategy is to save the current value when the cell begins being edited. This is done in an override of Edit. Then in a Paint override, checks are done to see if there is a click in the checkbox cell or if the space bar is hit. If either of these situations happen when the cell is actively being edited, the bool value is changed and the event fired.

Permalink
How can I make my grid never have an active edit cell and always select whole rows (as in a browser-type grid)

TFor a single row select datagrid, you can get both these behaviors by using a custom column style and overriding its Edit method. In your override, handle unselecting and
selecting the current row, and DO NOT call the base class. Not calling the base class keeps the cell from becoming active. Here is a code snippet suggesting how this might be done. You can download a full working project (CS, VB).

public class DataGridNoActiveCellColumn : DataGridTextBoxColumn
{
  private int SelectedRow = -1;
  protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly,string instantText,bool cellIsVisible)
  {
    //make sure previous selection is valid
    if(SelectedRow > -1 && SelectedRow < source.List.Count + 1)
      this.DataGridTableStyle.DataGrid.UnSelect(SelectedRow);
    SelectedRow = rowNum;
    this.DataGridTableStyle.DataGrid.Select(SelectedRow);
  }
}

If you want to handle multi-selections, then there is more work to be done. One solution is to still override Edit as above, but have an empty implementation. Do not have the code the handles the selected row and do not call the baseclass. To handle the selections in this case, subclass the datagrid and override its OnMouseDown virtual method to change all cell clicks into row header clicks. Also override OnCurrentCellChanged to handle moving the current cell with the keyboard. You can download a sample (C#, VB) that implements this functionality.

Permalink
How do I display a column of buttons such as pushbuttons or combobox buttons

This sample (download C#, download VB) derives two custom columnstyles that display buttons. One displays a pushbutton with the cell text used as the button label. The second columnstyle displays text plus a dropdown button similar to a combobox button. Both columnstyles have an event that your form can handle to respond to clicks on the buttons. The row and column of the click are passed as part of the event arguments.

Both columnstyles derive from DataGridTextBoxColumn, and override Paint and Edit. The Edit override does not call the baseclass to avoid allowing the cell going into the edit mode. In the Paint override, the text is drawn, and a bitmap showing the button face is drawn.

There is no mouse handling within a columnstyle. To catch the click action, the columnstyle must handle the datagrid’s MouseDown and MouseUp events. In the columnstyle handlers for these events, the handler draws the depressed button as well as firing the columnstyle’s ColumnButtonClick event.

Your handler for this ColumnButtonClick event should take whatever action as a result of the buttonclick. In the sample projects, the handler just displays a messagebox.

Permalink
How can I place text in the rowheader column of my datagrid

There is no text property exposed for a rowheader cell. But you can handle the Paint event and draw header text yourself. You can download sample projects (C#, VB) that illustrate one technique for doing so.

The sample loads the datagrid in the form’s Load event. In addition, this event is used to set the rowheaderwidth of the datagrid, and to remember the point where cell 0,0 is located. This point will allow us to find the toprow number when we need it to start drawing the header text. A handler for the datagrid’s Paint event is used to draw the text. It finds the toprow using the point from the original cell 0,0, and using the toprow determines the correct text for each rowheader. Finally, to avoid the complication of the user changing rowheights, we derive a new grid to prevent this.

private void dataGrid1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
  int row = TopRow();
  int yDelta = dataGrid1.GetCellBounds(row, 0).Height + 1;
  int y = dataGrid1.GetCellBounds(row, 0).Top + 2;
  
  CurrencyManager cm = (CurrencyManager) this.BindingContext[dataGrid1.DataSource, dataGrid1.DataMember];
  while(y < dataGrid1.Height - yDelta && row < cm.Count)
  {
    //get & draw the header text...
    string text = string.Format('row{0}', row);
    e.Graphics.DrawString(text, dataGrid1.Font, new SolidBrush(Color.Black), 12, y);
    y += yDelta;
    row++;
  }
}

Here is a datagrid with red row headers containing text.

Permalink
How can I enable column selections in my datagrid

Here is a sample (C#, VB) that has column selections implemented. It derives a datagrid, adds a columns selection array list, and maintains this list in an override of OnMouseDown. Then to handle actually drawing the selected columns, it uses a derived column style, and sets the backBrush color and foreBrush color to the selection values in the override of the Paint method when it needs to draw a selected cell.

As an alternative to drawing the selection in a derived column style, you could just draw the selection in your derived grid by overriding OnPaint, calling the base class and then looping through the columns selections, filling a rectangle over the selected columns. The problem with this technique is that you would either have to redraw text in column with the correct forecolor, or you would have to use an alphablended color to let the text show through the selection rectangle. Using an alphablended color would eliminate the need to a column style override, but would give a different appearance than the ’standard’ selections that you see in a datagrid. The sample allows the row selections to continue to work so the column style override makes sense here.

Permalink
How can I specially color only the currentcell of my readonly datagrid

You can use the first technique listed in this FAQ: How do I color an individual cell depending upon its value or some external method?

The idea is to derive a custom columnstyle. override its Paint method to specially color the background of the currentcell. Also, override the Edit to avoid the current cell becoming active. Below is a code snippet showing how you can do this. You can also download samples(C#, VB).

Public Class DataGridColoredTextBoxColumn
   Inherits DataGridTextBoxColumn
   
   Private column As Integer ’ column where this columnstyle is located...
   Public Sub New()
      column = -2
   End Sub

  Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal source As CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As Brush, ByVal foreBrush As Brush, ByVal alignToRight As Boolean)

    Try
      Dim grid As DataGrid = Me.DataGridTableStyle.DataGrid

      ’first time set the column properly
      If column = -2 Then
        Dim i As Integer
        i = Me.DataGridTableStyle.GridColumnStyles.IndexOf(Me)
        If i > -1 Then
          column = i
        End If
      End If

      If grid.CurrentRowIndex = rowNum And grid.CurrentCell.ColumnNumber = column Then
        backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal)
        foreBrush = New SolidBrush(Color.White)
      End If
     Catch ex As Exception
      ’ empty catch 
    Finally
              ’ make sure the base class gets called to do the drawing with
              ’ the possibly changed brushes
         MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight)
      End Try

   End Sub

   Protected Overloads Overrides Sub Edit(ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle, ByVal [readOnly] As Boolean, ByVal instantText As String, ByVal cellIsVisible As Boolean)
      ’do nothing... don’t call the basecalss
   End Sub
End Class
Permalink
How can I prevent a particular cell from being editable

You can do this by deriving a custom column style and overriding its virtual Edit member. Below is an override that will prevent the cell in row 1 of the column from getting the edit focus. You can paste this code in the DataGridDigitsTextBoxColumn sample to see it work.

If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires right before the call to the baseclass in the Edit override. This would allow the handler of the event to set the enable value depending upon the row and column parameters that are passed as part of the event args. You can download a sample (C#, VB) that implements this technique. The sample also fires the event right before painting the cell to decide whether to paint a gray background for the disabled cell. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values.

//this override will prevent the cell in row 1 from getting the edit focus
protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible)
{
  if(rowNum == 1)
    return;
  base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible);
}
Permalink
How do I prevent sorting a single column in my DataGrid

You can do this by deriving the DataGrid and overriding OnMouseDown. In your override, do a HitText and if the hit is on a column header that you do not want to sort, do not call the baseclass. Here is a code that sorts all columns except the second column.

[C#] 
//derived class 
public class MyDataGrid : DataGrid
{
    protected override void OnMouseDown(MouseEventArgs e)
    {
        Point pt = new Point(e.X, e.Y);
        DataGrid.HitTestInfo hti = this.HitTest(pt);
        if (hti.Type == HitTestType.ColumnHeader && hti.Column == 1)
        {
            //don't sort col 1 
            return; //don't call baseclass 
        }
        base.OnMouseDown(e);
    }
}

[VB.NET] 
'derived class 
Public Class MyDataGrid
    Inherits DataGrid
    Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    Dim pt As New Point(e.X, e.Y)
    Dim hti As DataGrid.HitTestInfo = Me.HitTest(pt)
        If hti.Type = HitTestType.ColumnHeader AndAlso hti.Column = 1 Then
    'don't sort col 1 
    Return 'don't call baseclass
        End If
        MyBase.OnMouseDown(e)
    End Sub 'OnMouseDown 
End Class 'MyDataGrid
Permalink
How do I use the DataColumn.Expression property to add a computed/combined column to my datagrid

The idea is to load your datatable in a normal fashion. Once the datatable is loaded, you can add an additional column that is computed from the other columns in your datatable. In the sample(CS, VB), we load the CustomerID, CompanyName, ContactName and ContactTitle from the Customers table in the NorthWind database. We then add an additional column that concatenates the ContactName and ContactTitle into one column.

To add the additional column, we create a DataColumn, set a mapping name, and then use the Expression property to define how the column is to be computed.

[C#]
  DataColumn dc = new DataColumn(''Contact'', typeof(string));
  dc.Expression = ''ContactName + ’:’ +ContactTitle'';
  _dataSet.Tables[''customers''].Columns.Add(dc);

[VB.NET]
  Dim dc As DataColumn
  dc = New DataColumn(''Contact'', GetType(System.String))
  dc.Expression = ''ContactName + ’:’ +ContactTitle''
  _dataSet.Tables(''customers'').Columns.Add(dc)

The sample actually shows two datagrids. The first one uses the default binding to display the entire table including our added column. In the second datagrid, we add a custom DataGridTableStyle to only display the CustomerID, CompanyName and our added column.

Permalink
How can I bind two datagrids in a Master-Detail relationship?

Please download this sample before reading the rest of this FAQ. Looking through the sample will help follow the description.

simpledata2.zip

What this boils down to is this:

1) Load both Master and Details queries in a dataset.

// I am using the SQL server NorthWind database 
this.dataAdapterMaster.Fill(this.dataSet, ''Customers'');
this.dataAdapterDetails.Fill(this.dataSet, ''Orders'');

2) Bind the master data grid to the Master dataset table.

// The master view
grid.DataSource = this.dataSet;  
grid.DataMember = ''Customers'';

3) Create a relationship that describes how the two tables relate to each other. A primary key foreign key relationship is defined by two attributes.

The primary key column in the master table
The foreign key column in the details table

The created relationship is added to the dataset.

this.dataSet.Relations.Add(''CustomersToOrders'', 
dataSet.Tables[''Customers''].Columns[''CustomerID''],
dataSet.Tables[''Orders''].Columns[''CustomerID'']);

4) Set the data member for the details table to be the name of relationship that was added to the dataset.

// The name of the relation is to be used as the DataMember for the
// details view
details.DataSource = this.dataSet;

// use the relationship called ''CustomersToOrders in the Customers table.
// Remember that we called the relationship ''CustomersToOrders''.
details.DataMember = ''Customers.CustomersToOrders'';
Permalink
How can I bind the datagrid to a datasource without using any wizards?

Here is a really simple data binding sample. Just drag and drop a datagrid onto a default Windows Forms application. Follow the steps below to bind this grid to the NorthWind db in SQL server.

Complete Sample: simpledata.zip

// Create a connection
SqlConnection connection = new SqlConnection(this.GetConnectionString());

// Create a data adapter. Think of the data adapter as an object that knows how to get the data from the
// data source into a dataset
SqlDataAdapter dataAdapter = new SqlDataAdapter(this.GetCommandText(), connection);

// fill the dataset using the data adapter
DataSet dataSet = new DataSet(''Customers'');
dataAdapter.Fill(this.dataSet, ''Customers'');

// bind to the grid
grid.DataSource = this.dataSet; // the big picture  
grid.DataMember = ''Customers''; // the specific table that we want to bind to

// The connection text looks like this
// If your SQL server is running on the default port, you can remove the port attribute.
private string GetConnectionString()
    {
      

                                                string server = ''your_server_name'';
      string serverPort = ''port_address'';
      string catalog = ''NorthWind'';
      string password = ''user_pass'';
      string userId =  ''user_name'';

      string connectionString = ''data source={0},{1};initial catalog={2};'' +
                    ''password={3}; user id={4}; packet size=4096'';
      
      return string.Format(connectionString, 
        server, serverPort, catalog, password, userId);
    }

// The command text looks like this

private string GetCommandText()
    {
      string commandText = ''Select * from customers'';
      return commandText;
    }
Permalink
How can I do cell by cell validation in a datagrid

There are problems trying to implement cell by cell validation using the grid’s Validating event architecture. The problem is that the grid is not the object
handling the data. Instead, a TextBox or some other control is the control managing the changing of the cell contents. One way to implement the validation at the grid level is to handle the CurrentCellChanged event, and if the previous cell’s value is not proper, then return to that cell. You can download a sample that implements this process. The sample only handles the validation from cell to cell movement. If you want to handle the validation when the user clicks on the forms Close button, then you would have to add a special event handler for this and do one last validation at this point.

Permalink
How can I restrict the keystrokes that will be accepted in a column of my datagrid

You can create a custom column style and handle the KeyPress event of its TextBox member. Below is the code showing how this might be done. You can also download a sample project (C#, VB) that shows an implementation of this idea.

public class DataGridDigitsTextBoxColumn : DataGridTextBoxColumn
{
  public DataGridDigitsTextBoxColumn(System.ComponentModel.PropertyDescriptor pd, string format, bool b)
      : base(pd, format, b)
  {
    this.TextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(HandleKeyPress);
  }

  private void HandleKeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
  {
    //ignore if not digit or control key
    if(!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
      e.Handled = true;

    //ignore if more than 3 digits
    if(this.TextBox.Text.Length >= 3 && !char.IsControl(e.KeyChar))
      e.Handled = true;
  }

  protected override void Dispose(bool disposing)
  {
    if(disposing)
      this.TextBox.KeyPress -= new System.Windows.Forms.KeyPressEventHandler(HandleKeyPress);

    base.Dispose(disposing);
  }
}
Permalink
How can I put a checkbox in a column of my DataGrid

You create a custom DataTableStyle that contains column styles for each column you want to display. You add the column styles in the order you want them to appear. Here are the steps to add an string column, an int column and a bool check column to a DataGrid. You can also download a working project.

// code assumes you have a DataSet named myDataSet, a table named ''EastCoastSales'' and a DataGrid myDataGrid
//STEP 1: Create a DataTable style object and set properties if required.
  DataGridTableStyle ts1 = new DataGridTableStyle();

  //specify the table from dataset (required step)
  ts1.MappingName = ''EastCoastSales'';
        
  // Set other properties (optional step)
        ts1.AlternatingBackColor = Color.LightBlue;

//STEP 2: Create a string column and add it to the tablestyle
  DataGridColumnStyle TextCol = new DataGridTextBoxColumn();
  TextCol.MappingName = ''custName''; //from dataset table
  TextCol.HeaderText = ''Customer Name'';
  TextCol.Width = 250;
  ts1.GridColumnStyles.Add(TextCol);

//STEP 3: Create an int column style and add it to the tablestyle
  //this requires setting the format for the column through its property descriptor
  PropertyDescriptorCollection pdc = this.BindingContext
       [myDataSet, ''EastCoastSales''].GetItemProperties();

  //now created a formated column using the pdc
  DataGridDigitsTextBoxColumn csIDInt = 
       new DataGridDigitsTextBoxColumn(pdc[''CustID''], ''i'', true);
  csIDInt.MappingName = ''CustID'';
  csIDInt.HeaderText = ''CustID'';
  csIDInt.Width = 100;
  ts1.GridColumnStyles.Add(csIDInt);

//STEP 4: Add the checkbox
  DataGridColumnStyle boolCol = new DataGridBoolColumn();
        boolCol.MappingName = ''Current'';
        boolCol.HeaderText = ''Info Current'';

                //uncomment this line to get a two-state checkbox
  //((DataGridBoolColumn)boolCol).AllowNull = false;

        boolCol.Width = 150;
        ts1.GridColumnStyles.Add(boolCol);

//STEP 5: Add the tablestyle to your datagrid’s tablestlye collection
  myDataGrid.TableStyles.Add(ts1);
Permalink
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

The TextBox property of the DataGridTextBoxColumn is ReadOnly, so you just cannot set a new derived TextBox into it. One solution is to derive a TextBox, and then have it use the derived TextBox in the DataGridTextBoxColumn instead of the ‘generic’ TextBox that is there. This is the same technique used in our combobox in a column sample. In that sample, the generic textbox was ‘replaced’ with a combobox. Here, we replace it with a derived TextBox where we can easily override virtual members.

 

A reasonable question is why not just use the event mechanism of the existing TextBox to modify behavior. Events like KeyPress would allow us to do some things PROVIDED they get hit. Due to the key processing architecture of the FrameWork, for some special keys, these key events are not always fired. And if they are fired, sometimes it is impossible to avoid the default processing if this is the intent you have. Overriding a virtual function, doing something special, and then NOT calling the baseclass is a standard way of avoiding default processing.

 

In this sample (both VB and CS), we override PreProcessMessage and avoid processing the Keys.Tab key. You can modify the code to not process Keys.F2 as well. DataGridTextBoxColumn is subclassed so it can use our derived TextBox.

Permalink
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
      source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush
      foreBrush, bool alignToRight)
    {
      // the idea is to conditionally set the foreBrush and/or backbrush
      // depending upon some criteria on the cell value
      // Here, we color anything that begins with a letter higher than ’F’
        try{
           object o = this.GetColumnValueAtRow(source, rowNum);
           if( o!= null)
           {
              char c = ((string)o)[0];
              if( c > ’F’)
              {
               // could be as simple as
               // backBrush = new SolidBrush(Color.Pink);
               // or something fancier...
                 backBrush = new LinearGradientBrush(bounds,
                    Color.FromArgb(255, 200, 200),
                    Color.FromArgb(128, 20, 20),
                     LinearGradientMode.BackwardDiagonal);
                 foreBrush = new SolidBrush(Color.White);
              }
           }
        }
        catch(Exception ex){ /* empty catch */ }
        finally{
           // make sure the base class gets called to do the drawing with
           // the possibly changed brushes
           base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight);
        }
     }
  }

[VB.NET}
Public Class DataGridColoredTextBoxColumn
  Inherits DataGridTextBoxColumn

  Public Sub New()
  End Sub

  Protected Overloads Overrides Sub Paint(ByVal g As Graphics, ByVal bounds As Rectangle, ByVal source As CurrencyManager, ByVal rowNum As Integer, ByVal backBrush As Brush, ByVal foreBrush As Brush, ByVal alignToRight As Boolean)

  ’ the idea is to conditionally set the foreBrush and/or backbrush
  ’ depending upon some crireria on the cell value
  ’ Here, we color anything that begins with a letter higher than ’F’
  Try
    Dim o As Object
    o = Me.GetColumnValueAtRow(source, rowNum)
    If (Not (o) Is Nothing) Then
      Dim c As Char
      c = CType(o, String).Substring(0, 1)
      If (c > ''F'') Then
         ’ could be as simple as
        ’ backBrush = new SolidBrush(Color.Pink);
        ’ or something fancier...
        backBrush = New LinearGradientBrush(bounds, Color.FromArgb(255, 200, 200), Color.FromArgb(128, 20, 20), LinearGradientMode.BackwardDiagonal)
        foreBrush = New SolidBrush(Color.White)
      End If
    End If
    Catch ex As Exception
      ’ empty catch 
    Finally
      ’ make sure the base class gets called to do the drawing with
       ’ the possibly changed brushes
      MyBase.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight)
    End Try

  End Sub
End Class

Method 2
To use some method to provide the cell color, you can use a similar technique as discussed above. But instead of setting the color based on cell value, call a delegate method instead. This delegate can be passed in using the constructor for the custom column style. You can download a sample that shows how this can be done.

Method 3
If you want a more flexible solution, you could expose an event as part of your derived columnstyle that fires in the Paint override. This would allow the handler of the event to set the color value depending upon the row and column parameters that are passed as part of the event args. You can download a sample (C#, VB) that implements this technique. The sample actually handles allowing a cell to be editable on a cell by cell basis through an event. This sample colors the back ground of the disabled cell gray. You could modify the eventargs to include a backcolor, and use this event to color cells based on row and column values removing the event call in the Edit override.

Permalink
How do I add an unbound column to my bound datagrid

The idea is to create the ’bound’ table in your dataset, and then add an extra ’unbound’ column. The steps are to derive a custom columnstyle that
overrides Paint where you calculate and draw the unbound value. You can also override Edit to prevent the user from selecting your unbound column. Then to get your datagrid to use this special column style, you create a tablestyle and add the column styles to it in the order you want the columns
to appear in the datagrid. Here are code snippets that derive the column and use the derived column. You can download a working sample.

  // custom column style that is an unbound column
  public class DataGridUnboundColumn : DataGridTextBoxColumn
  {
    protected override void Edit(System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Rectangle bounds, bool readOnly, string instantText, bool cellIsVisible) 
    { 
      //do not allow the unbound cell to become active
      if(this.MappingName == ''UnBound'')
        return; 
 
      base.Edit(source, rowNum, bounds, readOnly, instantText, cellIsVisible); 
    } 
 
    protected override void Paint(System.Drawing.Graphics g, System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager source, int rowNum, System.Drawing.Brush backBrush, System.Drawing.Brush foreBrush, bool alignToRight)
    {
    
      //clear the cell
      g.FillRectangle(new SolidBrush(Color.White), bounds);

      //compute & draw the value
      //string s = string.Format(''{0} row'', rowNum);
      // col 0 + 2 chars from col 1
      DataGrid parent = this.DataGridTableStyle.DataGrid;
      string s = parent[rowNum, 0].ToString() + ((parent[rowNum, 1].ToString())+ ''  '').Substring(0,2);
      Font font = new Font(''Arial'', 8.25f);
      g.DrawString(s, font, new SolidBrush(Color.Black), bounds.X, bounds.Y);
      font.Dispose();
      
    }
  }

  
  //code that uses this unbound column
  private void Form1_Load(object sender, System.EventArgs e)
  {
    // Set the connection and sql strings
    // assumes your mdb file is in your root
    string connString = @''Provider=Microsoft.JET.OLEDB.4.0;data source=C:\northwind.mdb'';
    string sqlString = ''SELECT * FROM customers'';

    OleDbDataAdapter dataAdapter = null;
    DataSet _dataSet = null;

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

      // Create data adapter object
      dataAdapter = new OleDbDataAdapter(sqlString, connection);
      
      // Create a dataset object and fill with data using data adapter’s Fill method
      _dataSet = new DataSet();
      dataAdapter.Fill(_dataSet, ''customers'');
      connection.Close();
    }
    catch(Exception ex)
    {  
      MessageBox.Show(''Problem with DB access-\n\n   connection: ''
        + connString + ''\r\n\r\n            query: '' + sqlString
        + ''\r\n\r\n\r\n'' + ex.ToString());
      this.Close();
      return;
    }

    // Create a table style that will hold the new column style 
    // that we set and also tie it to our customer’s table from our DB
    DataGridTableStyle tableStyle = new DataGridTableStyle();
    tableStyle.MappingName = ''customers'';

    // since the dataset has things like field name and number of columns,
    // we will use those to create new columnstyles for the columns in our DB table
    int numCols = _dataSet.Tables[''customers''].Columns.Count;

    //add an extra column at the end of our customers table
    _dataSet.Tables[''customers''].Columns.Add(''Unbound'');

    DataGridTextBoxColumn aColumnTextColumn ;
    for(int i = 0; i < numCols; ++i)
    {
      aColumnTextColumn = new DataGridTextBoxColumn();
      aColumnTextColumn.HeaderText = _dataSet.Tables[''customers''].Columns[i].ColumnName;

      aColumnTextColumn.MappingName = _dataSet.Tables[''customers''].Columns[i].ColumnName;
      tableStyle.GridColumnStyles.Add(aColumnTextColumn);

      //display the extra column after column 1.
      if( i == 1)
      {
        DataGridUnboundColumn unboundColStyle = new DataGridUnboundColumn();
        unboundColStyle.HeaderText = ''UnBound'';
        unboundColStyle.MappingName = ''UnBound'';
        tableStyle.GridColumnStyles.Add(unboundColStyle);        }
      }
      
      // make the dataGrid use our new tablestyle and bind it to our table
      dataGrid1.TableStyles.Clear();
      dataGrid1.TableStyles.Add(tableStyle);
      dataGrid1.DataSource = _dataSet.Tables[''customers''];

    }
  }
Permalink
How can I implement OLE Drag & Drop between a DataGrid and another OLE DnD object that supports the Text format

The attached samples (C#, VB) have a derived datagrid that supports OLE D&D with any OLE D&D provider that handles a Text formatted data object. The derived grid handles six events to allow it to be both a drop source and a drop target. The sample project has two datagrids where you can drag cell text back and forth. You can also open Excel, and drag text between Excel and either datagrid.

Here are the events that are handled in this sample.

  • MouseDown – Used to save the row and column of a mousedown, ’mousedowncell’.

  • MouseMove – Checks to see if you drag off the mousedowncell, and if so, starts a the DoDragDrop.

  • MouseUp – Used to reset the mousedowncell.

  • DragEnter – Checks to see if the data object has text, and if so, allows a Copy operation. (This could be changed to support Move/Copy.)

  • DragOver – Used to set a NoDrop cursor if you move over the mousedowncell (if mousedowncell has been set).

  • DragDrop – Used to drop the text into the datagrid.

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

Permalink
How can I move rows by dragging the row header cell

One way to implement this is to derive a DataGrid and override the virtual OnMouseDown, OnMouseMove and OnMouseUp methods. In your overrides, if the mousedown is on a row header, track the initial mousedown row, and as it moves, draw a line to indicate a target position. Then on the mouseup, handle moving the row.

You can download a sample (C#, VB) that illustrates how this might be done.

Permalink
How can I get the selected text in an active gridcell

You need to get the DataGridTextBoxColumn.TextBox member, and retrieve the SelectedText from it for the active cell. The sample shows how you can do this as part of handling this TextBox’s rightClick. This code assumes you have specifically added DataGridTextBoxColumn for each column style. You can also download a VB sample.

private void HandleMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
  if(e.Button == MouseButtons.Right)
  {
    DataGridTableStyle ts = dataGrid1.TableStyles[''customers''];
    DataGridTextBoxColumn cs = (DataGridTextBoxColumn)ts.GridColumnStyles[dataGrid1.CurrentCell.ColumnNumber];
    MessageBox.Show(''Selected: '' + cs.TextBox.SelectedText);

  }
}
Permalink
How do I hide the gridlines or set them to a particular color

You can download a working project. Below are VB code snippets showing how you might do these tasks.

’load a bitmap from an embedded resource
Dim Bmp As Bitmap 
’ from an embedded resource
Dim curName As String = ''BitmapVB.sync.bmp''
Dim strm As System.IO.Stream = Me.GetType().Assembly.GetManifestResourceStream(curName)
Bmp = New Bitmap(strm)
PictureBox1.Image = Bmp
.....
.....
’load a bitmap from a file
Dim Bmp As Bitmap = Image.FromFile(''c:\sync.bmp'')
PictureBox1.Image = Bmp
.....
.....
’modify a bitmap
Dim Bmp As Bitmap = PictureBox1.Image.Clone
Dim g As Graphics = Graphics.FromImage(Bmp)
Dim brush1 As SolidBrush = New SolidBrush(Color.Red)
g.DrawString(TextBox1.Text, TextBox1.Font, brush1, 10, 10)
PictureBox2.Image = Bmp
g.Dispose()
....
....
’save a bitmap as a file
Dim dlg As SaveFileDialog = New SaveFileDialog()
dlg.Title = ''Save BMP file''
dlg.InitialDirectory = ''c:\''
dlg.Filter = ''bmp files (*.bmp)|*.bmp|All files (*.*)|*.*''
If dlg.ShowDialog = DialogResult.OK Then
      PictureBox2.Image.Save(dlg.FileName
End If
Permalink
How can I get notification of the changing of a value in a column of comboboxes within my datagrid

This solution is based off the combobox for datagrid columns found in this FAQ. That solution replaces the standard textbox with a combobox. To get notifications of the changes, a delegate is passed into the constructor for the custom column style. This delegate is called anytime the combobox value changes. It passes the row number and value as arguments. You can download sample code (C#, VB) that shows the implementation.

Permalink
How can I get celltips or tooltips to vary from cell to cell in my DataGrid

One way to do this is to use a ToolTip control and reset the control text as the mouse moves from cell to cell. Below is a derived DataGrid class that implements this idea. The main points are:

Have members that track the current hitRow and hitCol where the mouse is.

  • In a MouseMove handler, do a HitTest on the mouse location to see if there is a new hit cell. If so, set the hitRow & hitCol, and hook the tooltip to hold your text according to the cell. In our sample, we just display the string value of the grid cell.
  • Finally, in the MouseMove handler, after setting a new text in the tooltip, set the tooltip active so it can show itself in due time.
public class DataGridCellTips : DataGrid
{
    private int hitRow;
    private int hitCol;
    private System.Windows.Forms.ToolTip toolTip1;

    public DataGridCellTips()
    {
        hitRow = -1;
        hitCol = -1;
        this.toolTip1 = new System.Windows.Forms.ToolTip();
        this.toolTip1.InitialDelay = 1000;
        this.MouseMove += new MouseEventHandler(HandleMouseMove);
    }

    private void HandleMouseMove(object sender, MouseEventArgs e)
    {
        DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y));
        if (hti.Type == DataGrid.HitTestType.Cell
            && (hti.Row != hitRow || hti.Column != hitCol))
        {     //new hit row 
            hitRow = hti.Row;
            hitCol = hti.Column;
            if (this.toolTip1 != null && this.toolTip1.Active)
                this.toolTip1.Active = false; //turn it off 
            this.toolTip1.SetToolTip(this, this[hitRow, hitCol].ToString());
            this.toolTip1.Active = true; //make it active so it can show itself 
            //Console.WriteLine('MouseMove '+ hitRow.ToString() + ' ' + hitCol.ToString()); 
        }
    }
}
Permalink
How can I make my DataGrid support a single select mode, and not the default multiselect mode

One way to do this is to derive a DataGrid, override its OnMouseDown and OnMouseMove methods. In the OnMouseDown, handle selecting and unselecting in your code without calling the base class if the click is on the header. In the OnMouseMove, don’t call the baseclass to avoid dragging selections. Below is a code snippet for a sample derived DataGrid. You can download a full project (C#, VB).

public class MyDataGrid : DataGrid
{
  private int oldSelectedRow = -1;
  
  protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
  {
    //don’t call the base class if left mouse down
    if(e.Button != MouseButtons.Left)
      base.OnMouseMove(e);
  }

  protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
  {
    //don’t call the base class if in header
    DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y));
    if(hti.Type == DataGrid.HitTestType.Cell) 
    {
      if(oldSelectedRow > -1)
        this.UnSelect(oldSelectedRow);
      oldSelectedRow = -1;
      base.OnMouseDown(e);
    }
    else if(hti.Type == DataGrid.HitTestType.RowHeader) 
    {
      if(oldSelectedRow > -1)
        this.UnSelect(oldSelectedRow);
      if((Control.ModifierKeys & Keys.Shift) == 0)
        base.OnMouseDown(e);
      else
        this.CurrentCell = new DataGridCell(hti.Row, hti.Column);
      this.Select(hti.Row);
      oldSelectedRow = hti.Row;
    }
  }
}
Permalink
How can I have a column of icons in my datagrid

You need to derive a custom column style, override its Paint method and draw the image. In the attached samples, (VB and C#), there are two custom column styles. One style is a stand-alone unbound column that just displays an image. The second custom column style adds the image to the left side of a bound column. In both cases, the actual image that is displayed is from an imagelist passed into the column in its constructor. The index of the image to be drawn on a particular row is determined by a delegate passed into the column style through its constructor.

Permalink
How can I put a combobox in a column of a datagrid

There are several ways to go about this task. The simplest way involves adding a single combobox to the DataGrid.Controls, and then selectively displaying it as needed when a combobox cell becomes the currentcell. All the work is done in a few event handlers and no overrides or derived classes are necessary. This technique is discussed in Microsoft KB article Q323167.

The other techniques require you to derive a columnstyle. Attached is a dropdown combobox sample (C#, VB) that shows how you can use a combobox in a datagrid. This implementation differs from other available columnstyle samples (gotdotnet.com and C# Corner ) in that it derives from DataGridTextBoxColumn. These other samples derive directly from DataGridColumnStyle, and thus have to add functionality that already exists in DataGridTextBoxColumn. 

This derived DataGridTextBoxColumn does not implement a databound combobox where you can set its DataSource, DisplayMember, and ValueMember to bind the combobox to a foreign table. If you need such a combobox, there is another sample link referenced at the end of this FAQ that does implement such a databound combobox.

This sample just attempts to replace the TextBox member of DataGridTextBoxColumn with a standard ComboBox member. Only two overrides need to be handled along with 2 events to get a functional implementation. 

Here are the notes from the code that list the 3 steps to add a combobox to your datagrid.

// Step 1. Derive a custom column style from DataGridTextBoxColumn 
//     a) add a ComboBox member 
// b) track when the combobox has focus in Enter and Leave events 
// c) override Edit to allow the ComboBox to replace the TextBox 
// d) override Commit to save the changed data 

// Step 2 - Use the combo column style 
// Add 1 col with combo style 
DataGridComboBoxColumn ComboTextCol = new DataGridComboBoxColumn();
ComboTextCol.MappingName = ''custCity'';
ComboTextCol.HeaderText = ''Customer Address'';
ComboTextCol.Width = 100;
ts1.GridColumnStyles.Add(ComboTextCol);

// Step 3 - Additional setup for Combo style 
// a) make the row height a little larger to handle minimum combo height 
ts1.PreferredRowHeight = ComboTextCol.ColumnComboBox.Height + 3;
// b) Populate the combobox somehow. It is a normal combobox, so whatever... 
ComboTextCol.ColumnComboBox.Items.Clear();
ComboTextCol.ColumnComboBox.Items.Add(''Chicago'');
ComboTextCol.ColumnComboBox.Items.Add(''Corvallis'');
ComboTextCol.ColumnComboBox.Items.Add(''Denver'');
ComboTextCol.ColumnComboBox.Items.Add(''Great Falls'');
ComboTextCol.ColumnComboBox.Items.Add(''Kansas City'');
ComboTextCol.ColumnComboBox.Items.Add(''Los Angeles'');
ComboTextCol.ColumnComboBox.Items.Add(''Raleigh'');
ComboTextCol.ColumnComboBox.Items.Add(''Washington'');

// c) set the dropdown style of the combo... 
ComboTextCol.ColumnComboBox.DropDownStyle = ComboBoxStyle.DropDownList;

Databound ComboBox Sample
To use a databound combobox, you have to add overrides for SetColumnValueAtRow and GetColumnValueAtRow to switch the DisplayMember and ValueMember as you get and set the data from the underlying table. Also, you cannot have the ComboBox bound with the same BindingContext to the same datasource as the datagrid. You can download a working project (C#, VB) that implements a databound combobox in a datagrid.

Thanks to Gerald Walsh for his suggestion to use the ComboBox.SelectionChangeCommitted event to set the editing flag within our derived columnstyle class.

Permalink
How do I prevent a click into the grid highlighting a cell

Set the grid’s Enabled property to false.

     dataGrid1.Enabled = false;
Permalink
How can I tell if the current row has changed and whether I am on the AddNew row or not

The DataGrid’s CurrentCellChanged event is hit even if you just change cells in the current row. If you want an event that is only hit when you change rows, then you have to look at the binding manager. This object has both a CurrentChanged event and a PositionChanged event which are hit when you change rows.

To decide whether you are on the AddNew row or not, you can again use the binding manager and compare the number of rows it returns with the number of rows in your data table. Below is some code snippets showing how you might get at this information.

private System.Windows.Forms.DataGrid dataGrid1;
private BindingManagerBase bindingManager;

private void Form1_Load(object sender, System.EventArgs e)
{
	// 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'); 
	dataGrid1.DataSource = ds.Tables['Employees'];
	bindingManager =  this.BindingContext[dataGrid1.DataSource];
	bindingManager.PositionChanged += new System.EventHandler(RowChanged);
}

private void RowChanged(object sender, System.EventArgs e)
{
	Console.WriteLine('RowChanged ' + bindingManager.Position.ToString() );
	bool lastRow = bindingManager.Count > ((DataTable)dataGrid1.DataSource).Rows.Count;
	
	if(lastRow)
		Console.WriteLine('lastRow');
}
Permalink
How do I programmatically select a row in DataGrid?

To programmatically select a row, you need to call:

[C#] 
//select row 1 
this.dataGrid1.Select(1); 

[VB.NET] 
'select row 1 
Me.dataGrid1.Select(1)
Permalink
How can I programatically set the rowheight of a row in my DataGrid

One way you can do this is to use reflection to access the DataGrid internal row objects which are not publicly exposed. This solution was suggested by Matthew Benedict in a private communication.

Here are both VB and C# sample projects showing how you might do this. The sample provides a class that you can instantiate by passing a DataGrid, creating a rowHeights object. Once you create this object, you can use an indexer on the object to set and get the rowheights.

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

Permalink
When I click on a row header, the row is selected and no cell is active. How can I do this programmatically

This code adds a method to a derived grid that simulates a mouseclick on the rowheader of the row passed into the method.

	public class MyDataGrid : DataGrid
	{
			
		public const int WM_LBUTTONDOWN = 513; // 0x0201 
		public const int WM_LBUTTONUP = 514; // 0x0202 
		
		[System.Runtime.InteropServices.DllImport('user32.dll')]
		static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);

		public void ClickRowHeader(int row)
		{
			//get a point to click
			Rectangle rect = this.GetCellBounds(row, 0);
			Int32 lparam = MakeLong(rect.Left - 4, rect.Top + 4);
		
			//click it
			SendMessage( this.Handle, WM_LBUTTONDOWN, 0, lparam);
			SendMessage( this.Handle, WM_LBUTTONUP, 0, lparam);
		}

		static int MakeLong(int LoWord, int HiWord)
		{
			return (HiWord << 16) | (LoWord & 0xffff);
		}
		
	}
.....
.....
	//usage - myDataGrid is of type MyDataGrid.
	private void button2_Click(object sender, System.EventArgs e)
	{
		myDataGrid.ClickRowHeader(2);
	}
Permalink
How can I translate a point to a cell in the datagrid

If the point, say pt, is in screen coordinates, you can use code such as

Point p1 = dataGrid1.PointToClient(pt);
MessageBox.Show(dataGrid1.HitTest(p1).ToString());

If you are using context menus for catching right-clicks in the grid, you have to do a little work to remember where the original right-click point was as the point passed in through the menu event arguments may not reflect this original click. One solution is to remember the click in the grid MouseDown event, and then use the code above to retrieve the grid cell from within the menu handler.

Point rightMouseDownPoint;

private void dataGrid1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
        rightMouseDownPoint = Cursor.Position;
}

private void menuItem4_Click(object sender, System.EventArgs e)
{
    Point pt = dataGrid1.PointToClient(rightMouseDownPoint);
    MessageBox.Show(dataGrid1.HitTest(pt).ToString());
}
Permalink
I have a derived DataGridColumnStyle. From within my Paint override, how can I get at other values in the DataGrid

You can get a reference to the DataGrid with code
such as:

            Dim grid As DataGrid = Me.DataGridTableStyle.DataGrid

Once you have the grid, you can use an indexer to get the value of any
particular column on the same row.

            Dim someValue as Object = grid(rowNum, 5)  ’ value in column 5....
Permalink
How can I tell whether a scrollbar is visible in my DataGrid is visible

If you are using a derived DataGrid, then you can check the Visible property on the protected VertScrollBar property of DataGrid. So, you could check Me.VertScrollBar.Visible from within your derived DataGrid.

To check it without access to the protected scrollbar properties is a little more work, but possible. One technique is to loop through the Controls property of the DataGrid looking for the scrollbar, and then checking its visible property at that time.

[C#]
	//sample usage
	bool vSrollBarVisible = this.IsScrollBarVisible(this.dataGrid1);
	.....

	private bool IsScrollBarVisible(Control aControl)
	{
		foreach(Control c in aControl.Controls)
		{
			if (c.GetType().Equals(typeof(VScrollBar)))
			{
				return c.Visible;
			}
		}
		return false;
	}

[VB.NET]
	’sample usage
	Dim vScrollBarVisible = Me.IsScrollBarVisible(Me.DataGrid1)
	......

	Private Function IsScrollBarVisible(ByVal aControl As Control) As Boolean
		Dim c As Control
		For Each c In aControl.Controls
			If c.GetType() Is GetType(VScrollBar) Then
				Return c.Visible
			End If
		Next
		Return False
	End Function
Permalink
How can I prevent all the cells in my DataGrid from being edited without deriving GridColumnStyle

One solution is to remove all the editing controls from the DataGrid.Controls collection. Without these controls, the grid contents cannot be edited. There is a technical problem that requires a little care. You do not want to delete all the controls in DataGrid.Controls as you would lose your scrollbars.

The code below deletes all controls except the scrollbars. Alternatively, you could also loop through the controls and only delete the TextBox.

[C#]
ArrayList al = new ArrayList();
foreach(Control c in this.dataGrid1.Controls)
{
	if(c.GetType() == typeof(VScrollBar) || c.GetType() == typeof(HScrollBar))
	{
		al.Add(c);
	}
}
this.dataGrid1.Controls.Clear();
this.dataGrid1.Controls.AddRange((Control[]) al.ToArray(typeof(Control)));

[VB.NET]
Dim al As New ArrayList()
Dim c As Control
For Each c In  Me.dataGrid1.Controls
	If c.GetType() = GetType(VScrollBar) Or c.GetType() = GetType(HScrollBar) Then
		al.Add(c)
	End If
Next c
Me.dataGrid1.Controls.Clear()
Me.dataGrid1.Controls.AddRange(CType(al.ToArray(GetType(Control)), Control()))
Permalink
How do I move columns in a datagrid

The columns appear in the order that their column styles were added to the tablestyle being used by the grid.
If you want to change this order, you would need to create a new table style, and add the columnstyles in the
order you want things to appear. Here is some code snippets that suggest how to do this.

[C#]
public void MoveColumn(DataGrid _dataGrid, string _mappingName, int fromCol, int toCol)
{
	if(fromCol == toCol) return;

	DataGridTableStyle oldTS = _dataGrid.TableStyles[_mappingName];
	DataGridTableStyle newTS = new DataGridTableStyle();
	newTS.MappingName = _mappingName;

	for(int i = 0; i < oldTS.GridColumnStyles.Count; ++i)
	{
		if(i != fromCol && fromCol < toCol)
			newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]);
		if(i == toCol)
			newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[fromCol]);
		if(i != fromCol && fromCol > toCol)
			newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]);	
	}

	_dataGrid.TableStyles.Remove(oldTS);
	_dataGrid.TableStyles.Add(newTS);
}

//sample usage
private void button1_Click(object sender, System.EventArgs e)
{
	MoveColumn(myDataGrid, 'Customers', 3, 1);
}

[VB.NET]
Public Sub MoveColumn(_dataGrid As DataGrid, _mappingName As String, fromCol As Integer, toCol As Integer)
	If fromCol = toCol Then
		Return
	End If 
	Dim oldTS As DataGridTableStyle = _dataGrid.TableStyles(_mappingName)
	Dim newTS As New DataGridTableStyle()
	newTS.MappingName = _mappingName
   
	Dim i As Integer
	i = 0
   	While i < oldTS.GridColumnStyles.Count
		If i <> fromCol And fromCol < toCol Then
			newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(i))
		End If
		If i = toCol Then
			newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(fromCol))	
		End If
		If i <> fromCol And fromCol > toCol Then
			newTS.GridColumnStyles.Add(oldTS.GridColumnStyles(i))
		End If
		i = i + 1
	End While 
	_dataGrid.TableStyles.Remove(oldTS)
	_dataGrid.TableStyles.Add(newTS)
End Sub ’MoveColumn

’sample usage
Private Sub button1_Click(sender As Object, e As System.EventArgs)
	MoveColumn(myDataGrid, 'Customers', 3, 1)
End Sub ’button1_Click
Permalink
How can I add a DateTimePicker column style to the DataGrid?

This MSDN reference shows how how to create DateTimePicker column style in the DataGrid.

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

When you select a row in the DataGrid and scroll it out of view using the mouse wheel, you cannot get it back into view. The following is a workaround posted by one Windows Forms User:

[C#]
this.dataGrid1.MouseWheel+=new MouseEventHandler(dataGrid1_MouseWheel);

private void dataGrid1_MouseWheel(object sender, MouseEventArgs e)
{
	this.dataGrid1.Select();

}

[VB.NET]
AddHandler Me.dataGrid1.MouseWheel, addressof dataGrid1_MouseWheel
 
Private  Sub dataGrid1_MouseWheel(ByVal sender As Object, ByVal e As MouseEventArgs)
	Me.dataGrid1.Select()
 
End Sub
Permalink
How can I make the DataGrid column be blank and not display (null) as the default value?

When you go to a new row in a DataGrid, the columns display (null). To prevent this behavior and make all the columns be empty, you can set the DefaultValue property of the DataColumn to be String.Empty.

Permalink
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(); 
                }
Permalink
In my datagrid, if I press tab to enter a column using a derived columnstyle, the column does not receive focus. Why

This behavior can be seen when you embedded a control like a textbox or combobox in your derived GridColumnStyle. If you press the tabkey slowly, you may see the cell get focus on the downkey and the cell lose focus on the upkey. One way to avoid this problem is to subclass the embedded control, and override its WndProc method, ignoring the KeyUp.

[C#]
public class MyCombo : ComboBox
{
         private const int WM_KEYUP = 0x101;

         protected override void WndProc(ref System.Windows.Forms.Message m)
         {
                  if(m.Msg == WM_KEYUP)
                  {
                           return;  //ignore the keyup
                  }
                  base.WndProc(ref m);
         }
}

[VB.NET]
Public Class MyTextBox
	Inherits TextBox
	Private WM_KEYUP As Integer = &H101
 
	Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
		If m.Msg = WM_KEYUP Then
			Return ’ignore the keyup
		End If
		MyBase.WndProc(m)
	End Sub ’WndProc
End Class ’MyTextBox
Permalink
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

Set the

 DataGrid.AllowNavigation

property to false.

Permalink
How do I format a date column in a datagrid

If you have added a table style to your datagrid (so individual column styles have been generated), then you can use code such as this to set the Format property of the particular column style.

[C#]
	//add format col 3 columnstyle where column 3 holds a date...

	DataGridTextBoxColumn dgtbc;
	dgtbc = dataGrid1.TableStyles[0].GridColumnStyles[3] as DataGridTextBoxColumn;

	if(dgtbc != null)
	 	dgtbc.Format = 'g'; // or 'u' or whatever format you want to see

[VB.NET]
	’add format col 3 columnstyle where column 3 holds a date...

	Dim dgtbc as DataGridTextBoxColumn 
	dgtbc = CType(dataGrid1.TableStyles(0).GridColumnStyles(3),  DataGridTextBoxColumn)

	If Not dgtbc is Nothing Then
	 	dgtbc.Format = 'g'  ’ or 'u' or whatever format you want to see
	End If
Permalink
How do I programmatically determine the selected rows in a datagrid

The method DataGrid.IsSelected can tell you if a particular row is selected.
So, you could use IsSelected in a loop through all your rows to finds if
multiple rows have been selected. Depending upon the size of your datagrid,
this may be a viable solution. If not, you could track the selections
yourself by monitoring the key actions and the mouse actions. This would be
more work. Thanks to John Hughes to the suggestion to use the dataview.

[C#]
public ArrayList GetSelectedRows(DataGrid dg)
{
	ArrayList al = new ArrayList();
	CurrencyManager cm = (CurrencyManager)this.BindingContext[dg.DataSource, dg.DataMember];    
    	DataView dv = (DataView)cm.List;
    
	for(int i = 0; i < dv.Count; ++i)
	{
		if(dg.IsSelected(i))
			al.Add(i);
	}
	return al;
}

private void button1_Click(object sender, System.EventArgs e)
{
	string s = 'Selected rows:';
	foreach(object o in GetSelectedRows(dataGrid1))
	{
		s+=''+o.ToString();
	}
	MessageBox.Show(s);
}

[VB.NET]
	Public Function GetSelectedRows(ByVal dg As DataGrid) As System.Collections.ArrayList
		Dim al As New ArrayList()

		Dim cm As CurrencyManager = Me.BindingContext(dg.DataSource, dg.DataMember)
		Dim dv As DataView = CType(cm.List, DataView)
   
		Dim i As Integer
		For i = 0 to dv.Count - 1
			If dg.IsSelected(i) Then
				al.Add(i)
			End If
		End Next
		Return al
	End Function ’GetSelectedRows
	
	 Private Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
		 Dim s As String = 'Selected rows:'
		Dim o As Object
		For Each o In GetSelectedRows(dataGrid1)
			s += ' ' + o.ToString()
		Next o
		MessageBox.Show(s)
	 End Sub ’button1_Click
Permalink
How can I use events to restrict key input to grid cells

If you make sure your DataGrid is using a DataGridTableStyle, then you can access the TextBox through the GridColumnStyles collection and hook the event there. Here is some code….

[C#]
	//in formload
	this.dataGrid2.DataSource = this.dataSet11.Customers; // set the data source

            	//make sure grid has a tablestyle
	DataGridTableStyle ts = new DataGridTableStyle();
	ts.MappingName = this.dataSet11.Customers.TableName;
	this.dataGrid2.TableStyles.Add(ts);

	//now we can wire up wire up events for columns 1 and 4 ....
	DataGridTextBoxColumn tbc = (DataGridTextBoxColumn)ts.GridColumnStyles[0];
	tbc.TextBox.KeyPress += new KeyPressEventHandler(CellKeyPress);

	tbc = (DataGridTextBoxColumn)ts.GridColumnStyles[3];
	tbc.TextBox.KeyPress += new KeyPressEventHandler(CellKeyPress);.....

	//the handler
	private void CellKeyPress(object sender, KeyPressEventArgs e)
	{
		//don’t allow 1’s
		if(e.KeyChar == ’1’)
		e.Handled = true;
	}

[VB.NET]
	 ’in formload
	Me.dataGrid2.DataSource = Me.dataSet11.Customers ’ set the data source
	
	’make sure grid has a tablestyle
	Dim ts As New DataGridTableStyle()
	ts.MappingName = Me.dataSet11.Customers.TableName
	Me.dataGrid2.TableStyles.Add(ts)

	’now we can wire up wire up events for columns 1 and 4 ....
	Dim tbc as DataGridTextBoxColumn = CType(ts.GridColumnStyles(0), DataGridTextBoxColumn)
	AddHandler tbc.TextBox.KeyPress, AddressOf CellKeyPress

	tbc = CType(ts.GridColumnStyles(3), DataGridTextBoxColumn)
	AddHandler tbc.TextBox.KeyPress, AddressOf CellKeyPress
	.....

	’the handler
	Private Sub CellKeyPress(sender As Object, e As KeyPressEventArgs) 
   		’don’t allow 1’s
		If e.KeyChar = '1'c Then
			e.Handled = True
		End If
	End Sub ’CellKeyPress
Permalink
How can I get a CheckBox column in a DataGrid to react to the first click

When you first click into a checkbox column, the checked state of the cell does not change. One way you can make the checked state change on the first click is to handle the grid’s MouseUp event, and change the check value there.

[VB.Net}
	Private myCheckBoxCol As Integer = 9  ’my checkbox column
	Private Sub DataGrid2_MouseUp(ByVal sender As Object,  ByVal e As MouseEventArgs) Handles DataGrid2.MouseUp
		Dim hti As DataGrid.HitTestInfo = Me.dataGrid2.HitTest(e.X, e.Y)
		Try
			If hti.Type = DataGrid.HitTestType.Cell AndAlso hti.Column = myCheckBoxCol Then
				Me.dataGrid2(hti.Row, hti.Column) = Not CBool(Me.dataGrid2(hti.Row, hti.Column))
			End If
		Catch ex As Exception
			MessageBox.Show(ex.ToString())
		End Try
	End Sub ’dataGrid2_MouseUp

[C#]
	private int myCheckBoxCol = 9; //my checkbox column

	private void dataGrid2_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
	{
		DataGrid.HitTestInfo hti   = this.dataGrid2.HitTest(e.X, e.Y);
		try
		{
			if( hti.Type == DataGrid.HitTestType.Cell && 
				hti.Column == myCheckBoxCol)
			{
				this.dataGrid2[hti.Row, hti.Column] = ! (bool) this.dataGrid2[hti.Row, hti.Column];
			}
		}																				catch(Exception ex)
		{
			MessageBox.Show(ex.ToString());
		}
	}
Permalink
How can I force the vertical scrollbar in my DataGrid to always be visible

Derive a DataGrid. In your derived grid, add a handler for the VertScrollBar.VisibleChanged event. In your handler, if the scrollbar is not visible, size it and position it, and then show it. The code below assumes no horizontal scrollbar is necessary. If it is present, you would have to adjust the sizing code.

C#
public class MyDataGrid : DataGrid
{

	public MyDataGrid()
	{
		//make scrollbar visible & hook up handler
		this.VertScrollBar.Visible = true;
		this.VertScrollBar.VisibleChanged += new EventHandler(ShowScrollBars);
	}

	private int CAPTIONHEIGHT = 21;
	private int BORDERWIDTH = 2;
	
	private void ShowScrollBars(object sender, EventArgs e)
	{
		if(!this.VertScrollBar.Visible)
		{
			int width = this.VertScrollBar.Width;
			this.VertScrollBar.Location = new Point(this.ClientRectangle.Width - width  - BORDERWIDTH, CAPTIONHEIGHT);
			this.VertScrollBar.Size = new Size(width, this.ClientRectangle.Height - CAPTIONHEIGHT - BORDERWIDTH);
			this.VertScrollBar.Show();				
		}
	}
}

VB.NET
Public Class MyDataGrid
	Inherits DataGrid
   
	Public Sub New()
		’make scrollbar visible & hook up handler
		Me.VertScrollBar.Visible = True
		AddHandler Me.VertScrollBar.VisibleChanged, AddressOf ShowScrollBars
	End Sub ’New
   
	Private CAPTIONHEIGHT As Integer = 21
	Private BORDERWIDTH As Integer = 2
   
	Private Sub ShowScrollBars(sender As Object, e As EventArgs)
		If Not Me.VertScrollBar.Visible Then
			Dim width As Integer = Me.VertScrollBar.Width
			Me.VertScrollBar.Location = New Point(Me.ClientRectangle.Width - width - BORDERWIDTH, CAPTIONHEIGHT)
			Me.VertScrollBar.Size = New Size(width, Me.ClientRectangle.Height - CAPTIONHEIGHT - BORDERWIDTH)
			Me.VertScrollBar.Show()
		End If
	End Sub ’ShowScrollBars
End Class ’MyDataGrid
Permalink
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;
	}
}
Permalink
How do I retrieve the current row from a DataTable bound to a DataGrid after the grid has been sorted

In an unsorted DataGrid bound to a DataTable, you can get a reference to a row in the DataTable through the DataGrid.CurrentRowIndex.

[C#]
	DataTable dt = (DataTable) this.dataGrid1.DataSource;
	DataRow dr = dt.Rows[this.dataGrid1.CurrentRowIndex);
[VB.NET]
	Dim dt As DataTable = Me.DataGrid1.DataSource
	Dim dr as DataRow = dt.Rows(Me.DataGrid1.CurrentRowIndex)

But if the grid has been sorted, you can no longer get at the current row in the table through the grid’s CurrentRowIndex. But for both unsorted and sorted grids, you can get at the current row through the BindingContext and the Current property of the BindingManagerBase.

[C#]
	BindingManagerBase bm = this.dataGrid1.BindingContextr[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
	DataRow dr = ((DataRowView)bm.Current).Row;
[VB.NET]
	Dim bm As BindingManagerBase = Me.DataGrid1.BindingContext(Me.DataGrid1.DataSource, Me.DataGrid1.DataMember)
	Dim dr As DataRow = CType(bm.Current, DataRowView).Row
Permalink
How can I catch when the user clicks off the grid, say to close the form

You can catch clicking off a DataGrid in the DataGrid.Validated event. But
this event is also hit when you use the Tab key to move around the grid. So,
if you want to catch it exclusively for clicking off the grid, you have to
ignore the event due to the tab. One way to do this is to derive DataGrid
and override ProcessDialogKey, noting whether the key is a tab before
processing it. Then in your Validated event handler, you can check for this
tab. Here are some snippets.

[C#]
//the handler that checks the derived grid’s field inTabKey
private void dataGrid1_Validated(object sender, System.EventArgs e)
{
	if(!this.dataGrid1.inTabKey)
	{
		Console.WriteLine( 'Clicked off grid');
	}
	else
		this.dataGrid1.inTabKey = false;
}

//derived class
public class MyDataGrid: DataGrid
{
	public bool inTabKey = false;

	protected override bool ProcessDialogKey(System.Windows.Forms.Keys
keyData)
	{
		inTabKey = keyData == Keys.Tab;
		return base.ProcessDialogKey( keyData);
	}
}

[VB.NET]
 ’the handler that checks the derived grid’s field inTabKey
Private Sub dataGrid1_Validated(sender As Object, e As System.EventArgs)
	If Not Me.dataGrid1.inTabKey Then
		Console.WriteLine('Clicked off grid')
	Else
		Me.dataGrid1.inTabKey = False
	End If
End Sub ’dataGrid1_Validated

’derived class
Public Class MyDataGrid
	Inherits DataGrid

	Public inTabKey As Boolean = False
     
	Protected Overrides Function ProcessDialogKey(keyData As System.Windows.Forms.Keys) As Boolean
		nTabKey = keyData = Keys.Tab
		Return MyBase.ProcessDialogKey(keyData)
	End Function ’ProcessDialogKey
End Class ’MyDataGrid
Permalink
How do I determine the DataGridTableStyle MappingName that should used for a DataGrid to make sure the grid uses my tablestyle

The DataGrid looks for a DataGridTableStyle.MappingName that is the type name of its datasource. So, depending upon what datasource you are using, this may be ‘ArrayList’ for a ArrayList, ‘MyCollection[]’ for an array of MyCollection objects, or ‘MyTableName’ for a datatable, or whatever.

Here is a code snippet provide by NoiseEHC on the microsoft.public.dotnet.framework.windowsforms.controls newsgroup that you can use to see exactly what mappingname is required for your datasource.

[C#]
//usage
ShowMappingName(dataGrid1.DataSource);

//implementation
void ShowMappingName(object src) 
{
	IList list = null;
	Type type = null;
	if(src is Array) 
	{
		type = src.GetType();
		list = src as IList;
	} 
	else 
	{
		if(src is IListSource)
			src = (src as IListSource).GetList();
		if(src is IList) 
		{
			type = src.GetType();
			list = src as IList;
		} 
		else 
		{
			MessageBox.Show('error');
			return;
		}
	}
	if(list is ITypedList) 
       		MessageBox.Show((list as ITypedList).GetListName(null));
	else 
		MessageBox.Show(type.Name);
}

[VB.NET]

Private Sub ShowMappingName(ByVal src As Object)
	Dim list As IList = Nothing
	Dim t As Type = Nothing

	If TypeOf (src) Is Array Then
		t = src.GetType()
		list = CType(src, IList)
	Else
  		If TypeOf src Is IListSource Then
			src = CType(src, IListSource).GetList()
		End If
		If TypeOf src Is IList Then
			t = src.GetType()
			list = CType(src, IList)
		Else
			MessageBox.Show('Error')
			Return
		End If
	End If

	 If TypeOf list Is ITypedList Then
		 MessageBox.Show(CType(list, ITypedList).GetListName(Nothing))
	Else
		MessageBox.Show(t.Name)
	End If
End Sub
Permalink
How can I make the Enter Key behave like the Tab Key and move to the next cell

You can override ProcessCmdKey, catch the Enter Key, and swap it for a Tab key by sending a Tab, and not processing the Enter Key.

[C#]
	public class MyDataGrid : DataGrid
	{
		protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
		{
			if(msg.WParam.ToInt32() == (int) Keys.Enter)
			{
				SendKeys.Send('{Tab}');
				return true;
			}
			return base.ProcessCmdKey(ref msg, keyData);
		}
	}

[VB.NET]
	Public Class MyDataGrid
		Inherits DataGrid
     
		Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, keyData As System.Windows.Forms.Keys) As Boolean
			If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
				SendKeys.Send('{Tab}')
				Return True
			End If
			Return MyBase.ProcessCmdKey(msg, keyData)
		End Function ’ProcessCmdKey

	End Class ’MyDataGrid
Permalink
How do I iterate through all the rows and columns in my datagrid

To iterate through all rows and columns in a WinForms DataGrid, you can access the underlying data using the grid’s view records and columns. The following code example demonstrates how to achieve this by looping through each record and column, retrieving its cell values and printing them to the console.

[C#]

private void button1_Click(object sender, EventArgs e)
{
     int rowCount = sfDataGrid1.View.Records.Count;
     int colCount = sfDataGrid1.Columns.Count;

     for (int row = 0; row < rowCount; row++)
     {
         var record = sfDataGrid1.View.Records[row].Data;
         for (int col = 0; col < colCount; col++)
         {
             var columnName = sfDataGrid1.Columns[col].MappingName;
             var property = record.GetType().GetProperty(columnName);
             var cellValue = property.GetValue(record);
             Console.Write(cellValue?.ToString() + " ");
         }
         Console.WriteLine();
     }
}

[VB.NET]

Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click
      Dim rowCount As Integer = sfDataGrid1.View.Records.Count
      Dim colCount As Integer = sfDataGrid1.Columns.Count

      For row As Integer = 0 To rowCount - 1
            Dim record = sfDataGrid1.View.Records(row).Data
            For col As Integer = 0 To colCount - 1
                  Dim columnName = sfDataGrid1.Columns(col).MappingName
                  Dim [property] = record.GetType().GetProperty(columnName)
                  Dim cellValue = [property].GetValue(record)
                  Console.Write(cellValue?.ToString() & " ")
            Next col
            Console.WriteLine()
      Next row
End Sub
Permalink
How can I prevent my user from sizing columns in my datagrid

You can do this by subclassing your grid and overriding OnMouseMove, and not calling the baseclass if the point is on the columnsizing border.

[C#]
public class MyDataGrid : DataGrid
{
	protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs e)
	{
		DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y));
		if(hti.Type == DataGrid.HitTestType.ColumnResize) 
		{
			return; //no baseclass call
		}
		base.OnMouseMove(e);
	}
}

[VB.NET]
Public Class MyDataGrid
	 Inherits DataGrid
	Protected Overrides  Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
		Dim hti As DataGrid.HitTestInfo =  Me.HitTest(New Point(e.X,e.Y)) 
		If hti.Type = DataGrid.HitTestType.ColumnResize Then
			Return ’no baseclass call
		End If
		MyBase.OnMouseMove(e)
	End Sub
End Class

The above code prevents the sizing cursor from appearing, but as Stephen Muecke pointed out to us, if the user just clicks on the border, he can still size the column. Stephen’s solution to this problem is to add similar code in an override of OnMouseDown.

[C#]
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
	DataGrid.HitTestInfo hti = this.HitTest(new Point(e.X, e.Y));
	if(hti.Type == DataGrid.HitTestType.ColumnResize) 
	{
		return; //no baseclass call
	}
	base.OnMouseDown(e);
}

[VB.NET]
Protected Overrides  Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
	Dim hti As DataGrid.HitTestInfo =  Me.HitTest(New Point(e.X,e.Y)) 
	If hti.Type = DataGrid.HitTestType.ColumnResize Then
		Return ’no baseclass call
	End If
	MyBase.OnMouseDown(e)
End Sub
Permalink
How do I set default values for new rows in my datagrid

You use the DataColumn.DefaultValue property to provide default values for new rows. You access this property through the DataTable associated with the DataGrid,

[C#]
	this.dataGrid1.DataSource = this._dataSet.Tables['orders'];
	....
	....
	this._dataSet.Tables['Orders'].Columns[1].DefaultValue = 'CustID';   // default value for column 1
	this._dataSet.Tables['Orders'].Columns['OrderDate'].DefaultValue = DateTime.Now;  // default value for OrderDate column

[VB.NET]
	Me.dataGrid1.DataSource = Me._dataSet.Tables('Orders')
	....
	....
	Me._dataSet.Tables('Orders').Columns(1).DefaultValue = 'CustID'   ’ default value for column 1
	Me._dataSet.Tables('Orders').Columns('OrderDate').DefaultValue = DateTime.Now   ’ default value for OrderDate column
Permalink
How do I determine whether a checkbox in my datagrid is checked or not

If the column is a boolean column, you can just cast the object returned by the indexer to a bool and use it.

if((bool)dataGridTopics[row, column])
	MessageBox.Show('I am true');
else
	MessageBox.Show('I am false');
Permalink
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

You can handle this by subclassing your grid and overriding either PreProcessMessage or ProcessDialogKey. The code below assumes your datasource is a dataview. If it is not, you could just remove that check

[C#]
public override bool PreProcessMessage( ref Message msg )
{
	Keys keyCode = (Keys)(int)msg.WParam & Keys.KeyCode;
	if(msg.Msg == WM_KEYDOWN 
		&& keyCode == Keys.Delete
		&& ((DataView) this.DataSource).AllowDelete)
	{
		if(MessageBox.Show('Delete this row?', '', MessageBoxButtons.YesNo) == DialogResult.No)
			return true;
	} 
	return base.PreProcessMessage(ref msg);
}

[VB.NET] (courtesy of Erik Johansen)
Public Class DataGrid_Custom
	Inherits DataGrid

	Private Const WM_KEYDOWN = &H100

	Public Overrides Function PreProcessMessage(ByRef msg As System.Windows.Forms.Message) As Boolean

		Dim keyCode As Keys = CType((msg.WParam.ToInt32 And Keys.KeyCode), Keys)
		If msg.Msg = WM_KEYDOWN And keyCode = Keys.Delete Then
			If MessageBox.Show('Delete This Row?', 'Confirm Delete', MessageBoxButtons.YesNo) = DialogResult.No Then
				Return True
			End If
		End If
		Return MyBase.PreProcessMessage(msg)

	End Function
End Class
Permalink
How can I get the number of rows in my DataGrid

One way you can do this is through the BindingManager.Count property.

[C#]
	int numRows = dataGridDetails.BindingContext[dataGridDetails.DataSource, dataGridDetails.DataMember].Count;

[VB.NET]
	Dim numRows as Integer = i dataGridDetails.BindingContext(dataGridDetails.DataSource, dataGridDetails.DataMember).Count;
Permalink
How do I get the row or column that has been clicked on?

You can use the DataGrid’s HitTest method, passing it a point in the grid’s client coordinate system,
and returning a HitTestInfo object that holds all the row and column information that you want.

[C#]
 //  X & Y are in the grid’ coordinates. If they are in screen coordinates, call dataGrid1.PointToClient method
System.Drawing.Point pt = new Point(X, 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());
}

[VB.NET]
 ’  X & Y are in the grid’ coordinates. If they are in screen coordinates, call dataGrid1.PointToClient method
Dim pt = New Point(X, Y)
Dim hti As DataGrid.HitTestInfo = dataGrid1.HitTest(pt)
If hti.Type = DataGrid.HitTestType.Cell Then
     	MessageBox.Show(dataGrid1(hti.Row, hti.Column).ToString())
Else
   	If hti.Type = DataGrid.HitTestType.ColumnHeader Then ’assumes datasource is a dataview
      		MessageBox.Show(CType(DataGrid1.DataSource, DataView).Table.Columns(hti.Column).ToString())
   	End If
End If
Permalink
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
Permalink
How do I programmatically scroll the datagrid to a particular row

The DataGrid has a protected GridVScrolled member that can be used to scroll the grid. To use it, you can derive from DataGrid and add a ScrollToRow method. Here is a code snippet.

	Public Class MyDataGrid
	     Inherits DataGrid
		Sub ScrollToRow(ByVal row As Integer)
			If Not Me.DataSource Is Nothing Then
				Me.GridVScrolled(Me, New ScrollEventArgs(ScrollEventType.LargeIncrement, row))
			End If
		End Sub
	End Class

This solution uses information provided by Daniel Herling (MS) in the microsoft.public.dotnet.framework.windowsforms.databinding newsgroup.

Permalink
How do I find the top-left visible cell in a datagrid

In a Windows Forms DataGrid, there is no property exposed that gives you this information. But here is little trick that will allow you to get the top-left visible cell.

1) Add a private member Point pointInCell00 to the form containing the datagrid.

2) After the datagrid has been initialized, but before your user has been able to scroll it (say at the end of the Form_Load event), use code such as this to initialize pointInCell00 to be a point in cell 0,0.

	pointInCell00 = new Point(dataGrid1.GetCellBounds(0,0).X + 4, dataGrid1.GetCellBounds(0,0).Y + 4);

3) Then add this method to return DataGridCell that is the top-left cell.

	public DataGridCell TopLeftVisibleCell()
	{
		DataGrid.HitTestInfo hti = dataGrid1.HitTest(this.pointInCell00);
		return new DataGridCell(hti.Row, hti.Column);
	}

	//sample usage...
	private void button1_Click(object sender, System.EventArgs e)
	{
		DataGridCell dgc = TopLeftVisibleCell();

		MessageBox.Show(dgc.RowNumber.ToString() + '   ' + dgc.ColumnNumber.ToString());
	}
Permalink
I want to do sort of a database join of two tables. How can I use data from two DataTables in a single DataGrid

Take a look at this Microsoft KB article, HOW TO: Extend the Windows Form DataGridTextBoxColumn to Display Data From Other Tables (C#,VB). It subclasses a DataGridColumnStyle and overrides GetColumnValueAtRow, Commit and ReadOnly to implement this behavior.

Permalink
How can I get rid of the error icon that appears when there is an editing error

Adam Chester gives this solution in a posting on the microsoft.public.dotnet.framework.windowsforms newgroup.

	DataTable dt = (DataTable)dataGrid1.DataSource;
	foreach(DataRow row in dt.GetErrors())
	{
		row.RowError = '';
		foreach(DataColumn col in dt.Columns)
  			row.SetColumnError(col, '');
	}
Permalink
How can I change the width of the row headers or hide them

You can get at the width and visibility of the header row/column through a DataGridTableStyle. DataGridTableStyle has properties such as RowHeadersVisible and RowHeadersWidth. DataGridTableStyle also controls things like selections colors and GridLine styles.

// Create a table style that will hold the new column style 
// that we set and also tie it to our customer’s table from our DB
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = 'customers';

//hide the column headers
tableStyle.ColumnHeadersVisible = false;

//change width of the row headers
tableStyle.RowHeadersWidth = 100;
   
// make the dataGrid use our new tablestyle and bind it to our table
dataGrid1.TableStyles.Clear();

dataGrid1.TableStyles.Add(tableStyle);
Permalink
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

As you tabbed to the right side of your grid, you have to tabbed through these zero width column and that is causing the tab key to appear not to work properly. One solution is to handle the grid’s CurrentCellChanged event, and if you are on a border cell (among the hidden columns), then explicitly set the proper currentcell.

//columns 3-6 are hidden with 0-column width...
private int LEFTHIDDENCOLUMN = 3;
private int RIGHTHIDDENCOLUMN = 6;
		
private void dataGrid1_CurrentCellChanged(object sender, System.EventArgs e)
{
	if(dataGrid1.CurrentCell.ColumnNumber == LEFTHIDDENCOLUMN)
		dataGrid1.CurrentCell = new DataGridCell(dataGrid1.CurrentCell.RowNumber + 1, 0);
	else if(dataGrid1.CurrentCell.ColumnNumber == RIGHTHIDDENCOLUMN)
		dataGrid1.CurrentCell = new DataGridCell(dataGrid1.CurrentCell.RowNumber, LEFTHIDDENCOLUMN - 1);
}
Permalink
How can I make the datagrid have no currentcell

There appears to be no method to turn off a currentcell. When a cell is being edited, it is the TextBox embedded in the columnstyle that has the focus, and is displaying the highlighted text. You will notice in this situation, if you click the grid’s title bar above the column headers, this TextEdit control loses focus, and the datagrid appears to have no current cell.

We can simulate this click from code, and use it to expose a method in our datagrid to SetNoCurrentCell. Below is some code to illustrate this idea.

public class MyDataGrid : DataGrid
{
	public const int WM_LBUTTONDOWN = 513; // 0x0201 
	public const int WM_LBUTTONUP = 514; // 0x0202 

	[System.Runtime.InteropServices.DllImport('user32.dll')]
	static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 
			lParam);

	public void SetNoCurrentCell()
	{
		//click on top left corner of the grid
		SendMessage( this.Handle, WM_LBUTTONDOWN, 0, 0);
		SendMessage( this.Handle, WM_LBUTTONUP, 0, 0);
	}
}

Here is some VB code.

Public Class MyDataGrid
  	Inherits DataGrid
  	
	Public WM_LBUTTONDOWN As Integer = 513 
   	Public WM_LBUTTONUP As Integer = 514

   	<System.Runtime.InteropServices.DllImport('user32.dll')> _
   	Shared Function SendMessage(hWnd As IntPtr, msg As Int32, wParam As Int32, lParam As Int32) As Boolean
   	End Function
   
   	Public Sub SetNoCurrentCell()
     		 ’click on top left corner of the grid
     		 SendMessage(Me.Handle, WM_LBUTTONDOWN, 0, 0)
      		SendMessage(Me.Handle, WM_LBUTTONUP, 0, 0)
   	End Sub ’SetNoCurrentCell
End Class ’MyDataGrid
Permalink
How do I set the width of a column in my DataGrid

To set a column width, your datagrid must be using a non-null DataGridTableStyle. Once this is in place, you can set the column width by first
getting the tablestyle and then using that object to obtain a column style with which you can set the width. Here are some code snippets showing
how you might do this.

//.... make sure your DataGrid is using a tablestyle
dataGrid1.DataSource = _dataSet.Tables['customers'];
DataGridTableStyle dgts = new DataGridTableStyle();
dgts.MappingName = 'customers';
dataGrid1.TableStyles.Add(dgts);

//......

//method to set a column with by colnumber
public void SetColWidth(DataGridTableStyle tableStyle, int colNum, int width)
{
	try
	{
		tableStyle.GridColumnStyles[colNum].Width = width;
		tableStyle.DataGrid.Refresh();
	}
	catch{} //empty catch .. do nothing
}

//....

// here is how you might call this method

private void button1_Click(object sender, System.EventArgs e)
{
	DataGridTableStyle tableStyle = dataGrid1.TableStyles['customers'];
	SetColWidth(tableStyle, 1, 200);
}
Permalink
How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter

Override the method ProcessKeyPreview in your DataGrid.

protected override bool ProcessKeyPreview(ref System.Windows.Forms.Message m)
{
	Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
	if((m.Msg == WM_KEYDOWN || m.Msg == WM_KEYUP)
		&& keyCode == Keys.Enter )
		return false;
	return true;
}
Permalink
How do I get the row and column of the current cell in my datagrid

You can use the CurrentCell property of the DataGrid.

private void button1_Click(object sender, System.EventArgs e)
{
	intcolNum = dataGrid1.CurrentCell.ColumnNumber;
	int rowNum = dataGrid1.CurrentCell.RowNumber;
	object cellValue = dataGrid1[rowNum, colNum];
	string s = string.Format('row={0}  col={1}  value={2}', rowNum, colNum, cellValue);
	MessageBox.Show(s);
}
Permalink
How do I make a field auto increment as new rows are added to my datagrid
	DataTable myTable = new DataTable('Customers');
	...
	DataColumn cCustID = new DataColumn('CustID', typeof(int));
	cCustID.AutoIncrement = true;
	cCustID.AutoIncrementSeed = 1;
	cCustID.AutoIncrementStep = 1;
     	myTable.Columns.Add(cCustID);
Permalink
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());
		}
	}
Permalink
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());
		}
	}
Permalink