Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Custom draw DataGridView column header (Excel 2007 style)

3.94/5 (9 votes)
28 Jan 2007CPOL3 min read 3   2.3K  
Overriding the cellpaint event of the DataGridView to give it a gradient background.

Sample Image - GradientColumnheader.png

Introduction

I've been using the CodeProject as a reliable source for quite some time now as I could depend on it in improving my developing skills, I decided it's time to do my own posting. I recently downloaded the new Office 2007 and I'm quite excited about the new layout. I work a lot with data, and wanted to improve the interface of the DataGridView, with column headers similar to the ones found in Excel 2007, with a bit of gradient colour in it.

Overview

I first experimented with the column header and figured out I had to switch of the EnableHeadersVisibleStyle option in order to get a color as background. However, a single color did not quite look like the gradient headers found in Office 2007.

So, I went further down and found the solution in overriding the cell paint event of the DataGridView. Column headers have a rowindex of -1, which makes it easy to limit the method to be executed only on the column header. There are quite a few options you can choose when overriding the cell paint event in order to get a custom drawn column header. I have chosen to make use of the Graphics object and the DrawImage method, which allowed me to use an image resource as the background. It opens possibilities for re-using the code as part of a skinning project, but that was beyond my needs for now.

Because of the use of the Graphics object, there are some alternatives for custom painting the DataGridView column header, like for instance using a LinearGradientBrush. To do that, simply replace the DrawImage part of the method with the FillRectangle method.

The code

In the opening event of the form, I put some code to fill the DataGridView. We'll skip that part here.

You could choose to create a class which inherits from the DataGridView and override the cell paint event there, or you could just override the event in the form class (I have chosen the second option for the example).

The code looks like this :

VB
Private Sub DataGridView1_CellPainting(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) _
        Handles DataGridView1.CellPainting
    If e.RowIndex = -1 Then
        Dim img As Image
        img = My.Resources.Header
        TekenAchtergrond(e.Graphics, img, e.CellBounds, 1)
        Dim format1 As StringFormat
        format1 = New StringFormat
        format1.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show
        Dim ef1 As SizeF = e.Graphics.MeasureString(e.Value, Me.Font, _
            New SizeF(CType(e.CellBounds.Width, Single), _
            CType(e.CellBounds.Height, Single)), format1)

        Dim txts As Size
        txts = Drawing.Size.Empty

        txts = Drawing.Size.Ceiling(ef1)
        e.CellBounds.Inflate(-4, -4)

        Dim txtr As Rectangle = e.CellBounds
        txtr = HAlignWithin(txts, txtr, ContentAlignment.MiddleCenter)
        txtr = VAlignWithin(txts, txtr, ContentAlignment.MiddleCenter)
        Dim brush2 As Brush
        format1 = New StringFormat
        format1.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show

        brush2 = New SolidBrush(Color.FromArgb(21, 66, 139))

        e.Graphics.DrawString(e.Value, Me.Font, brush2, _
                              CType(txtr, RectangleF), format1)
        brush2.Dispose()
        Dim recBorder As New Rectangle(e.CellBounds.X - 1, e.CellBounds.Y, _
                         e.CellBounds.Width, e.CellBounds.Height - 1)
        e.Graphics.DrawRectangle(Pens.LightSlateGray, recBorder)

        e.Handled = True
    End If
End Sub

In the CellPaint event, I get my header background from the resource files and pass it along with the Graphics object, the drawing rectangle, and an index parameter which can be used if the image file contains more than one template, for skinning purposes, to the Tekenachtergrond (I'm from Belgium, and tekenachtergrond is Dutch, meaning something like draw background) method. As I only use one template in this example, I set the index to 1.

The method, which is based on an article I found on this site, handles the skinning of controls (link to the article), along with some extra alignment methods and variables, looks like this:

VB
Private Sub TekenAchtergrond(ByVal g As Graphics, _
        ByVal obj As Image, ByVal r As Rectangle, _
        ByVal index As Integer)
    If (obj Is Nothing) Then
        Return
    End If
    Dim oWidth As Integer = obj.Width
    Dim lr As Rectangle = Rectangle.FromLTRB(0, 0, 0, 0)
    Dim r2 As Rectangle
    Dim r1 As Rectangle
    Dim x As Integer = ((index - 1) _
                * oWidth)
    Dim y As Integer = 0
    Dim x1 As Integer = r.Left
    Dim y1 As Integer = r.Top
    If ((r.Height > obj.Height) _
                AndAlso (r.Width <= oWidth)) Then
        r1 = New Rectangle(x, y, oWidth, lr.Top)
        r2 = New Rectangle(x1, y1, r.Width, lr.Top)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        r1 = New Rectangle(x, (y + lr.Top), oWidth, (obj.Height _
                        - (lr.Top - lr.Bottom)))
        r2 = New Rectangle(x1, (y1 + lr.Top), r.Width, (r.Height _
                        - (lr.Top - lr.Bottom)))
        If ((lr.Top + lr.Bottom) _
                    = 0) Then
            r1.Height = (r1.Height - 1)
        End If
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        r1 = New Rectangle(x, (y _
                        + (obj.Height - lr.Bottom)), oWidth, lr.Bottom)
        r2 = New Rectangle(x1, (y1 _
                        + (r.Height - lr.Bottom)), r.Width, lr.Bottom)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
    ElseIf ((r.Height <= obj.Height) _
                AndAlso (r.Width > oWidth)) Then
        r1 = New Rectangle(x, y, lr.Left, obj.Height)
        r2 = New Rectangle(x1, y1, lr.Left, r.Height)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        r1 = New Rectangle((x + lr.Left), y, (oWidth _
                        - (lr.Left - lr.Right)), obj.Height)
        r2 = New Rectangle((x1 + lr.Left), y1, (r.Width _
                        - (lr.Left - lr.Right)), r.Height)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        r1 = New Rectangle((x _
                        + (oWidth - lr.Right)), y, lr.Right, obj.Height)
        r2 = New Rectangle((x1 _
                        + (r.Width - lr.Right)), y1, lr.Right, r.Height)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
    ElseIf ((r.Height <= obj.Height) _
                AndAlso (r.Width <= oWidth)) Then
        r1 = New Rectangle(((index - 1) _
                        * oWidth), 0, oWidth, (obj.Height - 1))

        g.DrawImage(obj, New Rectangle(x1, y1, r.Width, r.Height), _
                    r1, GraphicsUnit.Pixel)
    ElseIf ((r.Height > obj.Height) _
                AndAlso (r.Width > oWidth)) Then
        'top-left
        r1 = New Rectangle(x, y, lr.Left, lr.Top)
        r2 = New Rectangle(x1, y1, lr.Left, lr.Top)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'top-bottom
        r1 = New Rectangle(x, (y _
                        + (obj.Height - lr.Bottom)), lr.Left, lr.Bottom)
        r2 = New Rectangle(x1, (y1 _
                        + (r.Height - lr.Bottom)), lr.Left, lr.Bottom)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'left
        r1 = New Rectangle(x, (y + lr.Top), lr.Left, (obj.Height _
                        - (lr.Top - lr.Bottom)))
        r2 = New Rectangle(x1, (y1 + lr.Top), lr.Left, (r.Height _
                        - (lr.Top - lr.Bottom)))
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'top
        r1 = New Rectangle((x + lr.Left), y, (oWidth _
                        - (lr.Left - lr.Right)), lr.Top)
        r2 = New Rectangle((x1 + lr.Left), y1, (r.Width _
                        - (lr.Left - lr.Right)), lr.Top)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'right-top
        r1 = New Rectangle((x _
                        + (oWidth - lr.Right)), y, lr.Right, lr.Top)
        r2 = New Rectangle((x1 _
                        + (r.Width - lr.Right)), y1, lr.Right, lr.Top)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'Right
        r1 = New Rectangle((x _
                        + (oWidth - lr.Right)), (y + lr.Top), lr.Right, (obj.Height _
                        - (lr.Top - lr.Bottom)))
        r2 = New Rectangle((x1 _
                        + (r.Width - lr.Right)), (y1 + lr.Top), lr.Right, (r.Height _
                        - (lr.Top - lr.Bottom)))
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'right-bottom
        r1 = New Rectangle((x _
                        + (oWidth - lr.Right)), (y _
                        + (obj.Height - lr.Bottom)), lr.Right, lr.Bottom)
        r2 = New Rectangle((x1 _
                        + (r.Width - lr.Right)), (y1 _
                        + (r.Height - lr.Bottom)), lr.Right, lr.Bottom)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'bottom
        r1 = New Rectangle((x + lr.Left), (y _
                        + (obj.Height - lr.Bottom)), (oWidth _
                        - (lr.Left - lr.Right)), lr.Bottom)
        r2 = New Rectangle((x1 + lr.Left), (y1 _
                        + (r.Height - lr.Bottom)), (r.Width _
                        - (lr.Left - lr.Right)), lr.Bottom)
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
        'Center
        r1 = New Rectangle((x + lr.Left), (y + lr.Top), (oWidth _
                        - (lr.Left - lr.Right)), (obj.Height _
                        - (lr.Top - lr.Bottom)))
        r2 = New Rectangle((x1 + lr.Left), (y1 + lr.Top), (r.Width _
                        - (lr.Left - lr.Right)), (r.Height _
                        - (lr.Top - lr.Bottom)))
        g.DrawImage(obj, r2, r1, GraphicsUnit.Pixel)
    End If
End Sub

How the result looks like, you can see in the attached screenshot on the top of this article, or you could just download the code and execute the exe file in the Release folder.

I hope this small piece of code is useful to someone like the many good code examples I have found at this site.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)