Introduction
Why reinvent the wheel when you can just get new hub caps? In this article, I will show how to gain access to the DataGridViewComboBoxEditing
control’s properties, methods, and events. Other examples I’ve seen show how to implement custom controls, but I didn’t want to create my own DropDownList
control. What I needed was to change the DisplayStyle
to DropDownButton
, alternate the BackColor
of the list, and set the ForeColor
to red if the list item begins with EXPIRED.
Using the code
The first item is a no brainer, the DisplayStyle
is one of the few properties exposed by the DataGridViewComboBoxColumn
. The other two, however, have no properties, methods, or events exposed that can allow these changes. With the ComboBox
control, you could use the DrawItem
event, or override the OnDrawItem
method to accomplish this. But the DataGridViewComboBoxColumn
does not expose the ComboBox
, in fact it is obscured by three classes. The DataGridViewComboBoxColumn
contains a DataGridViewComboBoxCell
object which contains a DataGridViewComboBoxEditingControl
object which inherits from the ComboBox
class. Urggggg!
When I first realized these successions, I nearly gave up on the idea, but then I thought just maybe I could use inheritance to gain access to the ComboBox
. This would be tricky, and the DataGridViewComboBoxColumn
and DataGridViewComboBoxCell
would have to allow alternate object types to be used.
I created three classes, DropDownListColumn
which inherits DataGridViewComboBoxColumn
, DropDownListCell
which inherits DataGridViewComboBoxCell
, and DropDownListEditingControl
which inherits DataGridViewComboBoxEditingControl
which inherits ComboBox
.
The DataGridViewComboBoxColumn
exposes the CellTemplate
property which gets or sets the object used in the DataGridView
cells. This property is changed in the default constructor within my new class, DropDownListColumn
.
Public Class DropDownListColumn
Inherits DataGridViewComboBoxColumn
Public Sub New()
Me.CellTemplate = New DropDownListCell
End Sub
End Class
The DataGridViewComboBoxCell
’s EditType
property returns the type of editing control used by the cell to edit the value. I override this property in my new class, DropDownListCell
, and return the type DropDownListEditingControl
. There is also the GetFormattedValue
function which allows the ForeColor
of the text in the cell to be altered. The ForeColor
needs to be altered in the drop down list as well.
Public Class DropDownListCell
Inherits DataGridViewComboBoxCell
Public Overrides ReadOnly Property EditType() As Type
Get
Return GetType(DropDownListEditingControl)
End Get
End Property
Protected Overrides Function GetFormattedValue( _
ByVal value As Object, ByVal rowIndex As Integer, _
ByRef cellStyle As System.Windows.Forms.DataGridViewCellStyle, _
ByVal valueTypeConverter As System.ComponentModel.TypeConverter, _
ByVal formattedValueTypeConverter As _
System.ComponentModel.TypeConverter, _
ByVal context As _
System.Windows.Forms.DataGridViewDataErrorContexts) As Object
Dim obj As Object = MyBase.GetFormattedValue( _
value, rowIndex, cellStyle, _
valueTypeConverter, formattedValueTypeConverter, _
context)
If Not IsNothing(obj) AndAlso obj.ToString.StartsWith("EXPIRED") Then
cellStyle.ForeColor = System.Drawing.Color.Red
End If
Return obj
End Function
End Class
The DataGridViewComboBoxEditingControl
is the final class we need to inherit from. First, we need to override the default constructor to set a couple of properties. We need to set DrawMode
to OwnerDrawFixed
. This causes the DrawItem
event to fire, allowing the developer to change the appearance of the list at runtime. We also need to set DropDownStyle
to DropDownList
, making the ComboBox
a DropDownList
. I could leave this property alone and set it in the properties page for the DropDownListColumn
, but I would have to do this each time I use the control.
To change the BackColor
and the ForeColor
, we need to override OnDrawItem
. Within the subroutine, we can use the System.Windows.Forms.DrawItemEventArgs
to render the FillRectangle
in either SystemColors.Window
or SystemColors.ControlLight
, two of my favorite colors. We can then look at the ToString
method of the list item object to see if it is prefixed with EXPIRED. If it is, use a Color.Red SolidBrush
, otherwise use a SystemColors.ControlText SolidBrush
to render the text. The ToString
method may need to be overridden in the list item's object class, or you may need to select another property to examine.
Public Class DropDownListEditingControl
Inherits DataGridViewComboBoxEditingControl
Public Sub New()
MyBase.New()
Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
Me.DropDownStyle = ComboBoxStyle.DropDownList
End Sub
Protected Overrides Sub OnDrawItem( _
ByVal e As System.Windows.Forms.DrawItemEventArgs)
Dim rec As New Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, _
e.Bounds.Height)
If Me.Enabled Then
If (e.State And DrawItemState.Focus) = DrawItemState.Focus Then
e.Graphics.FillRectangle( _
New SolidBrush(System.Drawing.SystemColors.Highlight), rec)
ElseIf (e.State And DrawItemState.ComboBoxEdit) = _
DrawItemState.ComboBoxEdit _
OrElse e.Index Mod 2 = 1 Then
e.Graphics.FillRectangle( _
New SolidBrush(System.Drawing.SystemColors.Window), rec)
Else
e.Graphics.FillRectangle( _
New SolidBrush(System.Drawing.SystemColors.ControlLight), rec)
End If
Else
e.Graphics.FillRectangle( _
New SolidBrush(System.Drawing.SystemColors.Control), rec)
End If
If e.Index > -1 Then
Dim obj As Object = Me.Items(e.Index)
If (e.State And DrawItemState.Focus) = DrawItemState.Focus Then
Dim HighlightedText As _
New SolidBrush(System.Drawing.SystemColors.HighlightText)
e.Graphics.DrawString(obj.ToString, e.Font, _
HighlightedText, rec)
ElseIf obj.ToString.StartsWith("EXPIRED") Then
Dim ExpireText As New SolidBrush(System.Drawing.Color.Red)
e.Graphics.DrawString(obj.ToString, e.Font, ExpireText, rec)
Else
Dim NormalText As New _
SolidBrush(System.Drawing.SystemColors.ControlText)
e.Graphics.DrawString(obj.ToString, e.Font, NormalText, rec)
End If
Else
Dim NormalText As New _
SolidBrush(System.Drawing.SystemColors.ControlText)
e.Graphics.DrawString("", e.Font, NormalText, rec)
End If
End Sub
End Class
There is not a lot of code to this technique, and it could be easily applied to other canned DataGridView
column controls. Additionally, this technique gains you access to many methods, properties, and events otherwise unavailable. Just think of all the hub caps one could create.
History