CheckBox in Header Cell of DataGridView Control

Normally the DataGridView control does not support adding a checkbox in the header cell.

But this doesn’t mean that it is impossible. You can create your own class that inherits from the DataGridViewColumnHeaderCell and use it.

Here’s one class I had created for myself when I needed it. Besides putting the CheckBox in the header cell, it also has a couple of properties and methods to make it behave the way we are used to seeing in normal windows applications. You can modify it as per your needs.

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.VisualStyles
Imports System.ComponentModel

Public Class DataGridViewCheckBoxHeaderCell
    Inherits DataGridViewColumnHeaderCell

    Private CheckBoxBounds As Rectangle
    Private CellLocation As New Point()
    Private CheckBoxState As CheckBoxState

    Public Delegate Sub DataGridViewCheckBoxHeaderCellEventHandler(ByVal sender As Object, ByVal e As DataGridViewCheckBoxHeaderCellEventArgs)
    Public Event CheckBoxClicked As DataGridViewCheckBoxHeaderCellEventHandler

#Region "Properties"
    Private _CheckBoxAlignment As HorizontalAlignment = HorizontalAlignment.Center
    Public Property CheckBoxAlignment() As HorizontalAlignment
        Get
            Return _CheckBoxAlignment
        End Get
        Set(ByVal value As HorizontalAlignment)
            If Not [Enum].IsDefined(GetType(HorizontalAlignment), value) Then
                Throw New InvalidEnumArgumentException("value", CInt(value), GetType(HorizontalAlignment))
            End If
            If _CheckBoxAlignment <> value Then
                _CheckBoxAlignment = value
                If Me.DataGridView IsNot Nothing Then Me.DataGridView.InvalidateCell(Me)
            End If
        End Set
    End Property

    Private _Checked As Boolean
    Public Property Checked() As Boolean
        Get
            Return _Checked
        End Get
        Set(ByVal value As Boolean)
            'If _Checked <> value Then 
            _Checked = value
            CheckState = IIf(_Checked, CheckState.Checked, CheckState.Unchecked)
            If Me.DataGridView IsNot Nothing Then Me.DataGridView.InvalidateCell(Me)
            'End If 
        End Set
    End Property

    Public Property CheckState() As CheckState
        Get
            Select Case CheckBoxState
                Case CheckBoxState.CheckedDisabled, CheckBoxState.CheckedHot, CheckBoxState.CheckedNormal, CheckBoxState.CheckedPressed
                    Return CheckState.Checked
                Case CheckBoxState.UncheckedDisabled, CheckBoxState.UncheckedHot, CheckBoxState.UncheckedNormal, CheckBoxState.UncheckedPressed
                    Return CheckState.Unchecked
                Case CheckBoxState.MixedDisabled, CheckBoxState.MixedHot, CheckBoxState.MixedNormal, CheckBoxState.MixedPressed
                    Return CheckState.Indeterminate
            End Select
        End Get
        Set(ByVal value As CheckState)
            If CheckState <> value Then
                If MyBase.DataGridView IsNot Nothing AndAlso MyBase.DataGridView.Enabled Then
                    'enabled state 
                    Select Case value
                        Case CheckState.Checked
                            CheckBoxState = CheckBoxState.CheckedNormal
                            _Checked = True
                        Case CheckState.Indeterminate
                            CheckBoxState = CheckBoxState.MixedNormal
                        Case CheckState.Unchecked
                            CheckBoxState = CheckBoxState.UncheckedNormal
                            _Checked = False
                    End Select
                Else
                    'disabled state 
                    Select Case value
                        Case CheckState.Checked
                            CheckBoxState = CheckBoxState.CheckedDisabled
                            _Checked = True
                        Case CheckState.Indeterminate
                            CheckBoxState = CheckBoxState.MixedDisabled
                        Case CheckState.Unchecked
                            CheckBoxState = CheckBoxState.UncheckedDisabled
                            _Checked = False
                    End Select
                End If
                If Me.DataGridView IsNot Nothing Then Me.DataGridView.InvalidateCell(Me)
            End If
        End Set
    End Property
#End Region

#Region "Methods"
    Protected Overridable Sub OnCheckBoxClicked(ByVal e As DataGridViewCheckBoxHeaderCellEventArgs)
        RaiseEvent CheckBoxClicked(Me, e)
    End Sub

    Public Sub CheckUncheckEntireColumn(ByVal checked As Boolean)
        Me.DataGridView.SuspendLayout()
        For Each row As DataGridViewRow In Me.DataGridView.Rows
            row.Cells(Me.ColumnIndex).Value = checked
        Next
        Me.DataGridView.ResumeLayout(True)
        Me.DataGridView.RefreshEdit()
    End Sub

    Public Sub RefreshCheckState()
        Dim newState As Boolean = Me.DataGridView.Rows(0).Cells(Me.ColumnIndex).EditedFormattedValue
        For Each row As DataGridViewRow In Me.DataGridView.Rows
            If row.Cells(Me.ColumnIndex).EditedFormattedValue <> newState Then
                Me.CheckState = CheckState.Indeterminate
                Exit Sub
            End If
        Next
        Me.Checked = newState
    End Sub
#End Region

#Region "Override"
    Protected Overloads Overrides Sub Paint(ByVal graphics As Graphics, ByVal clipBounds As Rectangle, ByVal cellBounds As Rectangle, ByVal rowIndex As Integer, ByVal dataGridViewElementState As DataGridViewElementStates, ByVal value As Object, _
                                            ByVal formattedValue As Object, ByVal errorText As String, ByVal cellStyle As DataGridViewCellStyle, ByVal advancedBorderStyle As DataGridViewAdvancedBorderStyle, ByVal paintParts As DataGridViewPaintParts)
        'checkbox bounds 
        Dim state As CheckBoxState = Me.CheckBoxState
        Dim checkBoxSize As Size
        Dim checkBoxLocation As Point

        cellLocation = cellBounds.Location
        checkBoxSize = CheckBoxRenderer.GetGlyphSize(graphics, state)
        Dim p As New Point()
        p.Y = cellBounds.Location.Y + (cellBounds.Height / 2) - (checkBoxSize.Height / 2)
        Select Case Me.CheckBoxAlignment
            Case HorizontalAlignment.Center
                p.X = cellBounds.Location.X + (cellBounds.Width / 2) - (checkBoxSize.Width / 2) - 1
            Case HorizontalAlignment.Left
                p.X = cellBounds.Location.X + 2
            Case HorizontalAlignment.Right
                p.X = cellBounds.Right - checkBoxSize.Width - 4
        End Select
        checkBoxLocation = p
        checkBoxBounds = New Rectangle(checkBoxLocation, checkBoxSize)

        'paint background 
        paintParts = paintParts And Not DataGridViewPaintParts.ContentForeground
        MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)

        'paint foreground 
        Select Case Me.CheckBoxAlignment
            Case HorizontalAlignment.Center
                cellBounds.Width = checkBoxLocation.X - cellBounds.X - 2
            Case HorizontalAlignment.Left
                cellBounds.X += checkBoxSize.Width + 2
                cellBounds.Width -= checkBoxSize.Width + 2
            Case HorizontalAlignment.Right
                cellBounds.Width -= checkBoxSize.Width + 4
        End Select
        paintParts = DataGridViewPaintParts.ContentForeground
        MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, dataGridViewElementState, value, "", errorText, cellStyle, advancedBorderStyle, paintParts)

        'paint check box           
        CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, state)
    End Sub

    Protected Overloads Overrides Sub OnMouseClick(ByVal e As DataGridViewCellMouseEventArgs)
        If e.Button = MouseButtons.Left Then
            'click on check box ? 
            Dim p As New Point(cellLocation.X + e.X, cellLocation.Y + e.Y)
            If checkBoxBounds.Contains(p) Then
                'raise event 
                RaiseCheckBoxClicked()
            End If
        End If
        MyBase.OnMouseClick(e)
    End Sub

    Protected Overloads Overrides Sub OnKeyDown(ByVal e As KeyEventArgs, ByVal rowIndex As Integer)
        If e.KeyCode = Keys.Space Then
            'raise event 
            RaiseCheckBoxClicked()
        End If
        MyBase.OnKeyDown(e, rowIndex)
    End Sub

    Public Overloads Overrides Function Clone() As Object
        Dim cell As DataGridViewCheckBoxHeaderCell = TryCast(MyBase.Clone(), DataGridViewCheckBoxHeaderCell)
        If cell IsNot Nothing Then
            cell.Checked = Me.Checked
        End If
        Return cell
    End Function
#End Region

#Region "Private"
    Private Sub RaiseCheckBoxClicked()
        'raise event 
        Dim e As New DataGridViewCheckBoxHeaderCellEventArgs(Not Me.Checked)
        Me.OnCheckBoxClicked(e)
        If Not e.Cancel Then
            Me.Checked = e.Checked
            Me.DataGridView.InvalidateCell(Me)
        End If
    End Sub
#End Region
End Class

#Region "EventArgs Class"
Public Class DataGridViewCheckBoxHeaderCellEventArgs
    Inherits CancelEventArgs

    Private _Checked As Boolean
    Public ReadOnly Property Checked() As Boolean
        Get
            Return _Checked
        End Get
    End Property

    Public Sub New(ByVal checkedValue As Boolean)
        MyBase.New()
        _Checked = checkedValue
    End Sub

    Public Sub New(ByVal checkedValue As Boolean, ByVal cancel As Boolean)
        MyBase.New(cancel)
        _Checked = checkedValue
    End Sub
End Class
#End Region

Sample Usage

Add the above class to your project.

Add a DataGridView control (DataGridView1) to your form and the following code:

Public Class Form1

    '' declare our header cell with checkbox 
    Dim WithEvents CheckBoxHeaderCell As DataGridViewCheckBoxHeaderCell

    Private Sub Form3_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        '' some dummy data source
        Dim di As New IO.DirectoryInfo("C:\")
        Dim ds = From fi In di.GetFiles _
                 Select New With {.FileName = fi.Name, .CreationTime = fi.CreationTime, _
                                  .LastAccessTime = fi.LastAccessTime, .LastModified = fi.LastWriteTime}
        DataGridView1.DataSource = ds.ToList
        DataGridView1.Columns.Insert(0, New DataGridViewCheckBoxColumn)

        '' our checkbox in header cell
        CheckBoxHeaderCell = New DataGridViewCheckBoxHeaderCell
        DataGridView1.Columns(0).HeaderCell = CheckBoxHeaderCell
    End Sub

    Private Sub CheckBoxHeaderCell_CheckBoxClicked(sender As Object, e As DataGridViewCheckBoxHeaderCellEventArgs) Handles CheckBoxHeaderCell.CheckBoxClicked
        CheckBoxHeaderCell.CheckUncheckEntireColumn(e.Checked)
    End Sub

    Private Sub DataGridView1_CellContentClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
        If e.ColumnIndex = CheckBoxHeaderCell.ColumnIndex Then CheckBoxHeaderCell.RefreshCheckState()
    End Sub
End Class

Run the program.

checkbox-in-header-cell-of-datagridview-control

Notice that your header cell has a checkbox. Click the checkbox in header cell and observe that the entire column gets checked/unchecked. Click the checkbox in any of the rows and observe that the checkbox in header cell sets its state accordingly.

Enjoy!Smile

 

About these ads

9 Responses to “CheckBox in Header Cell of DataGridView Control”

  1. Greg Says:

    This is absolutely fantastic, exactly what I was looking for. Example example, thank you very much.

  2. Beginner Says:

    Thank you a lot for this code! A lot of time has elapsed since publication date. Could you please give me a tip? How to assign a text to the header of the text box column?

  3. Tony Medina Says:

    Amigo estube probando el codigo y esta fantastico, pero el mismo tienen una pequeña falla, cuando activas o desactivas una sola columna, este evento no actualiza el el estado de la columna de tipo Check, pero esto es un error de la libreria de Visual Studio. afortunadamente buscando por internet pude encontrar la solucion, la siguiente parte de codigo la encontre del siguiente enlace:

    http://geeks.ms/blogs/fernandezja/archive/2008/07/15/datagridviewcheckboxcolumn-detectar-el-cambio-check-en-el-evento-cellvaluechanged.aspx

    Para no entrar en mas detalles si quieren saber el porque del error pueden leer el enlaces que les coloque

    en lugar de este evento:

    Private Sub DataGridView1_CellContentClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
            If e.ColumnIndex = CheckBoxHeaderCell.ColumnIndex Then CheckBoxHeaderCell.RefreshCheckState()
    End Sub
    

    Colocar este evento:

    Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
            If e.ColumnIndex = CheckBoxHeaderCell.ColumnIndex Then CheckBoxHeaderCell.RefreshCheckState()
    End Sub
    

    Y ademas colocar el siguiente evento:

    Private Sub DataGridView1_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
            If DataGridView1.IsCurrentCellDirty Then
                DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
            End If
    End Sub
    

    Gracias por el aporte.

  4. Tony Medina Says:

    My friend, i was testing the code and this fantastic, but it has a small flaw, when you activate or deactivate a single column, this event does not update the status column of type check, but this is a library error Visual Studio. fortunately searching the internet I could find the solution, the next part of the code I found the following link (in Spanish):

    http://geeks.ms/blogs/fernandezja/archive/2008/07/15/datagridviewcheckboxcolumn-detectar-el-cambio-check-en-el-evento-cellvaluechanged.aspx

    instead of this event:

    Private Sub DataGridView1_CellContentClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
        If e.ColumnIndex = CheckBoxHeaderCell.ColumnIndex Then CheckBoxHeaderCell.RefreshCheckState()
    End Sub
    

    Put this another event:

    Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
        If e.ColumnIndex = CheckBoxHeaderCell.ColumnIndex Then CheckBoxHeaderCell.RefreshCheckState()
    End Sub
    

    And additionally place this new event:

    Private Sub DataGridView1_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
        If DataGridView1.IsCurrentCellDirty Then
            DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
        End If
    End Sub
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 26 other followers

%d bloggers like this: