Friday, December 14, 2007

Multi Colored ComboboxListItems

Requirement

Consider this, a list of Students and their overall performance. For the sake of this example, I will simply rate the performance at two levels
  • 80% and above
  • below 80%
To easily discern between students at these performance levels, we can display the student listitems in different colors using the Combobox control.
A little about the Combobox Item rendering
Listitems in a combobox are rendered in one of two ways - an unselected listitem and a selected listitem.

Since we would like to render the Student Listitems based on the performance factor at two levels, we will require a total of four color combinations -
  • Students whose overall performance meets or exceeds 80%


    Normal unselected  
    Selected

  • Students whose overall performance is below 80%


    Normal unselected  
    Selected

Lets dig in

Having defined the colors that we can use for rendering the listitems, here are the steps that are required
  1. Change the DrawMode property of the Combobox to OwnerDrawVariable. The default value is Normal.


  2. Handle the MeasureItem event of the Combobox. This allows for manipulating the size of each Listitem. In this current example, I have simply set a fixed height and width, however based on the value of e.Index and its corresponding Listitem element, you can change these values if you wish to render the Listitems differently.

    Private Sub cmbStudents_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs) _
     Handles cmbStudents.MeasureItem

      e.ItemHeight = 18 'Pick this based on desired height of the listitem
      e.ItemWidth = cmbStudents.Width
    End Sub


  3. Handle the DrawItem event of the Combobox. Like the event name suggests, this event allows you to perform a custom draw of the Listitem every time the Listitem is displayed. For our example, it is sufficient to know that this event is fired each time a user expands the Combobox to view a list of items as well runs his mouse over the expanded list resulting in a hover selection of that listitem.

    Private Sub cmbStudents_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) _
     Handles cmbStudents.DrawItem
      If e.Index < 0 Then
        Exit Sub
      End If

      'Get the Student object that needs to be rendered using the e.Index
      Dim oStudent As Student = CType(cmbStudents.Items(e.Index), Student)
      Dim DisplayStr As String = oStudent.Name

      Dim r As RectangleF = New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)

      If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
        If oStudent.OverallPerformanceScore > 80 Then
          e.Graphics.FillRectangle(Brushes.Lavender, r)
          e.Graphics.DrawString(DisplayStr, e.Font, System.Drawing.Brushes.Blue, r)
        Else
          e.Graphics.FillRectangle(Brushes.AntiqueWhite, r)
          e.Graphics.DrawString(DisplayStr, e.Font, System.Drawing.Brushes.Red, r)
        End If
      Else
        If oStudent.OverallPerformanceScore > 80 Then
          e.Graphics.FillRectangle(Brushes.GhostWhite, r)
          e.Graphics.DrawString(DisplayStr, e.Font, System.Drawing.Brushes.Blue, r)
        Else
          e.Graphics.FillRectangle(Brushes.SeaShell, r)
          e.Graphics.DrawString(DisplayStr, e.Font, System.Drawing.Brushes.Red, r)
        End If
      End If

      ' Draw the focus rectangle if mouse hovers over the item.
      e.DrawFocusRectangle()
    End Sub


The Result




Student Class Listing


Private Class Student
  Private _Name As String
  Private _OverallPerformanceScore As Double

  Public Sub New(ByVal Name As String, ByVal OverallPerformanceScore As Double)
    _Name = Name
    _OverallPerformanceScore = OverallPerformanceScore
  End Sub

  Public ReadOnly Property Name() As String
    Get
      Return _Name
    End Get
  End Property

  Public ReadOnly Property OverallPerformanceScore() As Double
    Get
      Return _OverallPerformanceScore
    End Get
  End Property

  Public Overrides Function ToString() As String
    Return Name
  End Function
End Class

No comments: