Introduction
Drop down lists are great for selecting from a list of items. However, a plain text list can be difficult to read if there are a lot of items. Icons help to make a list easier to read, however the standard ComboBox control does not support images.
OwnerDraw
As with the ListView control that I customised here, the ComboBox can also be modified.
My new control supports not only adding icons, but also indenting and disabling specific items.

The first image shows multicoloured icons with randomly applied indenting. Indents can be any value you require. Only 0 and 1 are shown here. The second image shows all green icons, however items 3 and 4 are “disabled”. Notice that the icons and text appear greyed out.
The Code
As with the ListView control, a few more lines of code are required…
ComboBox Definition
# Define ComboBox control
$cmoComboBox = New-Object 'System.Windows.Forms.ComboBox'
# Add OwnerDraw code
$cmoComboBox.ItemHeight = '22'
$cmoComboBox.DrawMode = 'OwnerDrawFixed'
$cmoComboBox.Add_DrawItem($ComboIcons_OnDrawItem)
$cmoComboBox.Add_SelectedIndexChanged($ComboIcons_SelectedIndexChanged)
# Set ComboBox properties
$cmoComboBox.Location = ' 12, 65'
$cmoComboBox.Size = '370, 35'
$cmoComboBox.Font = $sysFont
$cmoComboBox.DropDownStyle = 'DropDownList'
# Add control to the main form
$MainFORM.Controls.Add($cmoComboBox)
OwnerDraw Code
# Required Bits
[int]$script:ComboIcons_SelectedItem = 0 # Keeps track of the currently selected item
[System.Collections.ArrayList]$IconComboItems = @{} # Holds all the custom items for the ComboxBox
Function New-IconComboItem { Return ( New-Object -TypeName PSObject -Property @{'Icon' = ''; 'Name' = ''; 'Text' = ''; 'Indent' = '0'; 'Enabled' = $True; } ) }
# Used to "disable" an image (turn it into a greyscale image of itself)
Function GreyScaleImage ([System.Drawing.Image]$Image)
{
If ([string]::IsNullOrEmpty($Image) -eq $True) { Return $Image }
[System.Drawing.Image] $newImage = New-Object 'System.Drawing.Bitmap'($Image.Width, $Image.Height)
[System.Drawing.Graphics] $graphics = [System.Drawing.Graphics]::FromImage($newImage)
[System.Drawing.Imaging.ColorMatrix] $matrix = New-Object 'System.Drawing.Imaging.ColorMatrix'
[System.Drawing.Imaging.ImageAttributes]$imgAttrib = New-Object 'System.Drawing.Imaging.ImageAttributes'
$matrix.Matrix00 = '0.0'; $matrix.Matrix10 = '1.0'; $matrix.Matrix11 = '1.0'; $matrix.Matrix12 = '1.0'; $matrix.Matrix22 = '0.0'; $matrix.Matrix33 = '0.5'
$imgAttrib.SetColorMatrix($matrix, [System.Drawing.Imaging.ColorMatrixFlag]::Default, [System.Drawing.Imaging.ColorAdjustType]::Bitmap)
$graphics.DrawImage($Image, (New-Object 'System.Drawing.Rectangle'(0, 0, $Image.Width, $Image.Height)), 0, 0, $newImage.Width, $newImage.Height, [System.Drawing.GraphicsUnit]::Pixel, $imgAttrib)
$graphics.Dispose()
Return $newImage
}
# Custom draws each cmoComboBox item
$ComboIcons_OnDrawItem = {
[System.Windows.Forms.DrawItemEventArgs]$e = $_
$e.DrawBackground()
$e.DrawFocusRectangle()
[System.Drawing.Rectangle]$bounds = $e.Bounds
If (($e.Index -gt -1) -and ($e.Index -lt $IconComboItems.Count))
{
$currItem = $IconComboItems[$e.Index]
[int] $indent = ($currItem.Indent * 14)
[System.Drawing.Image] $icon = $imgResult.Images[$currItem.Icon]
[System.Drawing.SolidBrush]$solidBrush = [System.Drawing.SolidBrush]$e.ForeColor
If ($currItem.Enabled -eq $False) {
$icon = (GreyScaleImage -Image $icon)
$solidBrush.Color = [System.Drawing.SystemColors]::GrayText
}
$iconRect = New-Object 'System.Drawing.RectangleF'((($bounds.Left) + 5 + $indent), (($bounds.Top) + 2), 16, 16)
$textRect = New-Object 'System.Drawing.RectangleF'((($bounds.Left) + ($iconRect.Width) + 9 + $indent), $bounds.Top, (($bounds.Width) - ($iconRect.Width) - 9 - $indent), $bounds.Height)
$format = New-Object 'System.Drawing.StringFormat'
$format.Alignment = [System.Drawing.StringAlignment]::Near
$format.LineAlignment = [System.Drawing.StringAlignment]::Center
If ($icon -ne $null) { $e.Graphics.DrawImage($icon, $iconRect) }
$e.Graphics.DrawString($currItem.Text, $e.Font, $solidBrush, $textRect, $format)
}
}
# Get the currently selected item (but only if not disabled)
$ComboIcons_SelectedIndexChanged = {
If ($cmoComboBox.SelectedIndex -lt 0) { Return }
If ($cmoComboBox.SelectedIndex -lt $IconComboItems.Count)
{
If ($IconComboItems[$cmoComboBox.SelectedIndex].Enabled -eq $false) { $cmoComboBox.SelectedIndex = $script:ComboIcons_SelectedItem }
Else { $script:ComboIcons_SelectedItem = $cmoComboBox.SelectedIndex }
}
$lbl_Description.Text = $($IconComboItems[$script:ComboIcons_SelectedItem])
}
ComboxBox List Items
There is a little more to adding items to the ComboBox list however. You need to create a new object, populate it your required values, then add that to the $IconComboItems
array. Once you have created all your items, add them to the ComboBox control
Single Item Example
# Define new custom ComboBox item
$newItem = New-IconComboItem
$newItem.Name = 'item01'
$newItem.Text = 'This is Item 01'
$newItem.Icon = 0
$newItem.Indent = 0
$newItem.Enabled = $True
# Add the new item to the array of items
[void]$IconComboItems.Add($newItem)
# Repeat the above until you have added all the items you need
# Add the array of items to the ComboBox itself
[void]$cmoComboBox.Items.AddRange($IconComboItems)
$cmoComboBox.SelectedIndex = 0
Coloured Folder Example (Screen Shot 1)
[string[]]$colors = @('Black', 'Blue', 'Cyan', 'Green', 'Grey', 'Orange', 'Pink', 'Red', 'White', 'Yellow')
0..9 | ForEach-Object -Process {
$tmp = New-IconComboItem
$tmp.Name = "Item$_"
$tmp.Text = $colors[$_]
$tmp.Icon = $_
If ($_ -gt 0) { $tmp.Indent = (Get-Random -Maximum 2 -Minimum 0) }
[void]$IconComboItems.Add($tmp)
$tmp = $null
}
[void]$cmoComboBox.Items.AddRange($IconComboItems)
$cmoComboBox.SelectedIndex = 0
Conclusion
Obviously there is a lot of code missing that is used in the screen shots, but the important part is there in order to get you up and running. Have a play, and let me know what you think in the comments below.
A full demo can be found on my GitHub