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

5. Windows Forms Datagrid

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



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


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

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

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


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



5.2 How can I autosize the rowheights in my datagrid?


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

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

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

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

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

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

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

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

}

g.Dispose();
}


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


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

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

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

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

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

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


5.4 How do I programmatically select a row in DataGrid?


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

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

Control related steps

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

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

Other steps

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


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


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

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

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


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


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


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


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


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


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

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

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

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

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

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

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

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

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

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

              }

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

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

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

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


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


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

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

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

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

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

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

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

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


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


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

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

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

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


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


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

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

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


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


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


    5.12 How do I hide a column?


    There are several ways to hide a column:

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

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

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

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


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

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


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


    We give three different methods for doing this.

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


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

    [C#]
         public class DataGridColoredTextBoxColumn : DataGridTextBoxColumn
         {
              protected override void Paint(System.Drawing.Graphics g,
                   System.Drawing.Rectangle bounds, System.Windows.Forms.CurrencyManager
                   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.


    5.14 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);


    5.15 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);
         }
    }


    5.16 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);


    5.17 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);
    }



    5.18 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


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


    5.20 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


    5.21 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;
              }



    5.22 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";



    5.23 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


    5.24 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"];

              }
         }



    5.25 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);
    }


    5.26 How can I prevent the Enter key from moving to the next cell when the user is actively editing the cell and presses Enter?


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


    5.27 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);
    }



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

  • 5.29 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");
    }


    5.30 How do I hide the gridlines or set them to a particular color?


    If your datagrid does not have a custom TableStyle associated with it, then there are two DataGrid properties that control the color and visibility of the gridlines.

         DataGrid..GridLineColor
         DataGrid.GridLineStyle

    If you have a custom TableStyle. you need to set the color within the TableStyle. Here is code that makes the line color the same as the background color, and hence hides the gridlines. You can download a sample.

    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";
         tableStyle.GridLineColor = dataGrid1.BackColor;
         
         dataGrid1.TableStyles.Add(tableStyle);
         dataGrid1.DataSource = _dataSet.Tables["customers"];

    }


    5.31 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);

         }
    }


    5.32 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");


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


    5.34 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



    5.35 After scrolling with the mouse wheel on a selected row in a DataGrid I cannot get it back into view. Is there a work around?


    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



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


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


    5.38 How can I put a combobox in a column of a datagrid?


    The CollectionEditor allows adding and removing items from a collection at design time. If the items in this collection implement IComponent or if they are derived from Component, the items in your collection can be persisted in code.

    Download collectioneditorsample.zip for a complete sample project.

    Here are some steps you should follow:
    1) Make sure your item is derived from Component or implements Icomponent.
    For example:


         public class SomeItem : Component
         {
              private string label = "";

              public SomeItem()
              {
              }

              public SomeItem(string label)
              {
                   this.label = label;
              }

              public string Label
              {
                   get
                   {
                        return label;
                   }
                   set
                   {
                        label = value;
                   }
              }

              public override string ToString()
              {
                   return String.Format("SomeItem: ( Label = '{0}' )", label);
              }
         }


    2) Next implement your Collection. You have to implement the Ilist interface. The CollectionEditor will determine the type of instances to be added to your collection using reflection inspecting the return type of the Item property (Indexer).


              [
              Description("The set of properties to be edited with CollectionEditor."),
              DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
              Editor(typeof(System.ComponentModel.Design.CollectionEditor),
                   typeof(System.Drawing.Design.UITypeEditor))
              ]
              public SomeItemCollection SomeItems
              {
                   get
                   {
                        if (someItemCollection == null)
                        {
                             someItemCollection = CreateItemCollection();
                        }
                        return someItemCollection;
                   }
              }

              protected SomeItemCollection CreateItemCollection()
              {
                   return new SomeItemCollection(this);
              }

              public class SomeItemCollection : IList
              {
                   // Fields
                   private SomeItemDisplayer owner;
                   public event EventHandler Changed;

                   // Constructors
                   public SomeItemCollection(SomeItemDisplayer owner)
                   {
                        this.owner = owner;
                   }

                   internal ArrayList InnerArray
                   {
                        get
                        {
                             return owner.someItems;
                        }
                   }
                   
                   public void OnChanged()
                   {
                        if (this.Changed != null)
                             this.Changed(this, EventArgs.Empty);
                   }

                   ///
                   /// The CollectionEditor will determine the type of objects to be created by
                   /// looking at the property type of the following method. CollectionEditor
                   /// internally uses reflection to get the PropertyInfo for the "Item" property.
                   /// This method must be public.
                   ///
                   public SomeItem this[int index]
                   {
                        set
                        {
                             if (value == null)
                                  throw new ArgumentNullException("value");

                             if (index < 0 || index >= this.InnerArray.Count)
                                  throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString()));

                             this.InnerArray[index] = value;
                             OnChanged();
                        }
                        get
                        {
                             if (index < 0 || index >= this.InnerArray.Count)
                                  throw new ArgumentOutOfRangeException("Invalid Argument {0}: {1}", "index", index.ToString());
                             return (SomeItem) this.InnerArray[index];
                        }
                   }

                   public void AddRange(object[] items)
                   {
                        InnerArray.AddRange(items);
                        OnChanged();
                   }

                   ///
                   /// This implementation for the Item property for the IList interface. It's
                   /// property type is object.
                   ///
                   object IList.this[int index]
                   {
                        set
                        {
                             // forward call to public indexer
                             this[index] = (SomeItem) value;
                        }
                        get
                        {
                             // forward call to public indexer
                             return this[index];
                        }
                   }

                   public /*IEnumerable*/ IEnumerator GetEnumerator()
                   {
                        return InnerArray.GetEnumerator();
                   }


                   public /*ICollection*/ int Count
                   {
                        get
                        {
                             return InnerArray.Count;
                        }
                   }

                   public /*IList*/ void RemoveAt(int index)
                   {
                        if (index < 0 || index >= this.InnerArray.Count)
                             throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}", "index", index.ToString()));

                        this.InnerArray.RemoveAt(index);
                        OnChanged();
                   }


                   public /*IList*/ void Remove(object value)
                   {
                        int n = this.InnerArray.IndexOf(value,0);
                        if (n != -1)
                             this.RemoveAt(n);
                   }


                   public /*IList*/ void Insert(int index, object item)
                   {
                        if (item == null)
                             throw new ArgumentNullException("item");
                        if (index < 0 || index > this.InnerArray.Count)
                             throw new ArgumentOutOfRangeException(String.Format("Invalid Argument {0}: {1}","index", index.ToString()));

                        this.InnerArray.Insert(index,item);
                        OnChanged();
                   }


                   public /*IList*/ int IndexOf(object value)
                   {
                        if (value == null)
                             throw new ArgumentNullException(String.Format("Invalid Argument {0}: {1}","value", "null"));
                        return this.InnerArray.IndexOf(value,0);
                   }


                   public /*IList*/ bool IsReadOnly
                   {
                        get
                        {
                             return false;
                        }
                   }

                   public /*IList*/ void Clear()
                   {
                        InnerArray.Clear();
                        this.owner.Invalidate();
                   }


                   public /*IList*/ bool Contains(object value)
                   {
                        return this.IndexOf(value) != -1;
                   }

                   void System.Collections.ICollection.CopyTo(Array dest, int index)
                   {
                        int count = this.InnerArray.Count;
                        for (int n1 = 0; n1 < count; n1++)
                             dest.SetValue(this.InnerArray[n1], (n1 + index));
                   }


                   int System.Collections.IList.Add(object item)
                   {
                        int n = this.InnerArray.Add(item);
                        OnChanged();
                        return n;
                   }


                   bool System.Collections.IList.IsFixedSize
                   {
                        get
                        {
                             return false;
                        }
                   }


                   bool System.Collections.ICollection.IsSynchronized
                   {
                        get
                        {
                             return false;
                        }
                   }


                   object System.Collections.ICollection.SyncRoot
                   {
                        get
                        {
                             return this;
                        }
                   }

              }


    3) Reference this collection in your control or component that should be designable. You need to supply a DesignerSerializationVisibility and an Editor attribute:


              private SomeItemCollection someItemCollection = null;
              ArrayList someItems = new ArrayList();
              
              [
              Description("The set of properties to be edited with CollectionEditor."),
              DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
              Editor(typeof(System.ComponentModel.Design.CollectionEditor),
                   typeof(System.Drawing.Design.UITypeEditor))
              ]
              public SomeItemCollection SomeItems
              {
                   get
                   {
                        if (someItemCollection == null)
                        {
                             someItemCollection = CreateItemCollection();
                        }
                        return someItemCollection;
                   }
              }

              protected SomeItemCollection CreateItemCollection()
              {
                   return new SomeItemCollection(this);
              }




    5.39 I want to do custom handling of special keys such as the Tab key or F2 in the TextBox of a column in the DataGrid. How do I subclass this TextBox to get at it virtual members?


    You have to access a property called the Binding Context and then retrieve the BindingContext associated with the dataset and data member that you used for binding. After you have access to this object you just set the position property. You can move backward and forward through the dataset.

    Download a working sample that shows this: simpledata5.zip


    form.BindingContext[this.dataSet, "Customers"].Position -= 1;


    Remember that when you scroll through the dataset all associated controls will scroll since they all depend on the same context. This is useful if you want to have several controls that display sections of a row operate in tandem.


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


    5.41 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;
              }
         }
    }



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


    Felix Wu gives this solution in the microsoft.public.dotnet.frameworks.windowsforms newgroup.

    You can download a VB.NET sample.

    Override the OnPaint event of the TextBox. For example:

    protected override void OnPaint(PaintEventArgs e)
    {
         SolidBrush drawBrush = new SolidBrush(ForeColor); //Use the ForeColor property
         // Draw string to screen.
         e.Graphics.DrawString(Text, Font, drawBrush, 0f,0f); //Use the Font property
    }

    Note: You need to set the ControlStyles to "UserPaint" in the constructor.

    public MyTextBox()
    {
         // This call is required by the Windows.Forms Form Designer.
         this.SetStyle(ControlStyles.UserPaint,true);

         InitializeComponent();

         // TODO: Add any initialization after the InitForm call
    }


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


    5.44 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

         Shared _
         Function SendMessage(hWnd As IntPtr, msg As Int32, wParam As Int32, lParam As Int32) As Boolean

         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


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


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


    5.46 I have hidden (column width = 0) columns on the right side of my datagrid, but tabbing does not work properly. How can I get tabbing to work?


    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);
    }


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


    5.48 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


    5.49 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);


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


    5.51 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;
    }



    5.52 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


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


    5.54 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);
         }


    5.55 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


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


    5.57 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, "");
         }


    5.58 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());
         }


    5.59 I want to display negative values with a CR suffix, instead of a minus sign. How can I perform custom formatting on the cells in my datagrid?


    Take a look at this Microsoft KB article, HOW TO: Extend the Windows Form DataGridTextBoxColumn Control to Custom-Format Data (Q318581). It subclasses a DataGridColumnStyle and overrides both GetColumnValueAtRow and Commit to implement this behavior.


    5.60 I want to do sort of a database join of two tables. How can I use data from two DataTables in a single DataGrid?


    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.


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


    5.62 How can I put up a confirmation question when the user tries to delete a row in the datagrid by clicking on the row header and pressing the Delete key?


    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)