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 :
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:
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
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)
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)
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)
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)
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)
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)
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)
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)
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.