(Print this page)

Showing images in a combo box
Published date: Thursday, August 17, 2017
On: Moer and Éric Moreau's web site

After a break in July (for many reasons – mainly missing time between projects at work and summer holidays!), I am back with another article.

By pure coincidence, my previous article (June) and this current one are both related to the combo box control.

This month, we will add images to the dropdown part of the combo. If you have read my June’s article, you will find that it is exactly the same mechanism to initialize and draw custom items that are used to be able to display images. It is in fact so close, that I will reuse the demo code from the June article as the base for this month. If you haven’t read last month article, you better take 5 minutes to review it as I won’t re-explain what was in there!

This month’s downloadable code

This month solution contains both VB and C# projects. The solution was created using Visual Studio 2017 but the code should work as well in older version of the.Net Framework.

Figure 1: The demo application in action

Modifying the ComboBoxItem class

Each item displayed in the combo box, will now be able to show a small icon on the left of the text. To achieve this behavior, we need to add a new property to the ComboBoxItem class created last month.

This new property is called ImageIndex and it contains the index of the image we will display.

The ComboboxItem class now reads like this:

Public Class ComboboxItem

    Public Text As String
    Public Value As Object
    Public Selectable As Boolean = True
    Public ImageIndex As Integer = -1

    Public Overrides Function ToString() As String
        Return Text
    End Function

End Class
public class ComboboxItem
{

    public string Text;
    public object Value;
    public bool Selectable = true;
    public int ImageIndex=-1;

    public override string ToString()
    {
        return Text;
    }

}

Setting the ImageList control

Because we will need some icons, surely the easiest way is to drop an Image List control on our form and populate it with the images we want.

I have dropped that control to my form and added 5 random icons in its Images collection just for the demo. Notice that like most index value, the images collection is 0 based.

Figure 2: Setting up the Image List control

Filling the combo box

Last month, when we wanted to fill our combo box with values, we needed to add instances of the ComboboxItem into the Items collection. We need to do exactly the same thing here and just add the index of the related image (added to Image List Images collection) which is a new property of the ComboboxItem class.

Private Sub FillCombo()
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed

    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test1", .Value = 1, .ImageIndex = 0})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test2", .Value = 2, .ImageIndex = 1})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test3", .Value = 3, .ImageIndex = 2})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test4", .Value = 4, .ImageIndex = 3, .Selectable = False})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test5", .Value = 5, .ImageIndex = 4})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test6", .Value = 6, .ImageIndex = 0})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test7", .Value = 7})
    ComboBox1.Items.Add(New ComboboxItem With {.Text = "Test8", .Value = 8})
    ComboBox1.Items.Add("a regular item") 'to test a plain old item

    'Test manual setting of an unselectable item
    mblnManualSet = True
    ComboBox1.SelectedIndex = 3
    mblnManualSet = False
End Sub
private void FillCombo()
{
    ComboBox1.DrawMode = DrawMode.OwnerDrawFixed;

    ComboBox1.Items.Add(new ComboboxItem { Text = "Test1", Value = 1, ImageIndex = 0 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test2", Value = 2, ImageIndex = 1 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test3", Value = 3, ImageIndex = 2 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test4", Value = 4, ImageIndex = 3, Selectable = false });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test5", Value = 5, ImageIndex = 4 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test6", Value = 6, ImageIndex = 0 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test7", Value = 7 });
    ComboBox1.Items.Add(new ComboboxItem { Text = "Test8", Value = 8 });
    ComboBox1.Items.Add("a regular item"); //to test a plain old item

    ComboBox1.DrawItem += ComboBox1_DrawItem;
    ComboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;

    //Test manual setting of an unselectable item
    _blnManualSet = true;
    ComboBox1.SelectedIndex = 3;
    _blnManualSet = false;
}

Notice that we don’t have to set an image if we don’t need one for some items. It will just not show any icon for that specific item but will keep the text correctly aligned.

Adding icons when drawing the items

The last thing we need to do is to draw the icons in the drop down if the ImageIndex is set. This is done in the DrawItem event handler just like we used in the previous article to fade the un-selectable items (which we still do).

Private Sub ComboBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ComboBox1.DrawItem
    e.DrawBackground()
    e.DrawFocusRectangle()
    If (e.Index < 0) Then
        e.Graphics.DrawString(Text, e.Font, New SolidBrush(e.ForeColor), e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top)
    Else
        Dim objItem As ComboboxItem = TryCast(ComboBox1.Items(e.Index), ComboboxItem)
        If objItem IsNot Nothing Then

            'draw the icon if there is one
            If (objItem.ImageIndex >= 0) Then
                imageList1.Draw(e.Graphics, e.Bounds.Left, e.Bounds.Top, objItem.ImageIndex)
            End If

            If (Not objItem.Selectable) Then
                'draw an unselectable item
                e.Graphics.DrawString(objItem.Text, myFont2, Brushes.LightSlateGray, e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top)
            Else
                'draw a regular item
                e.Graphics.DrawString(objItem.Text, myFont, Brushes.Black, e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top)
            End If
        Else
            e.Graphics.DrawString(ComboBox1.Items(e.Index).ToString(), myFont, New SolidBrush(e.ForeColor), e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top)
        End If
    End If
End Sub
private void ComboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    e.DrawBackground();
    e.DrawFocusRectangle();
    if (e.Index < 0)
        e.Graphics.DrawString(Text, e.Font, new SolidBrush(e.ForeColor), e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top);
    else
    {
        if (ComboBox1.Items[e.Index].GetType() == typeof(ComboboxItem))
        {
            ComboboxItem objItem = (ComboboxItem)ComboBox1.Items[e.Index];

            //draw the icon if there is one
            if (objItem.ImageIndex >= 0)
            {
                imageList1.Draw(e.Graphics, e.Bounds.Left, e.Bounds.Top, objItem.ImageIndex);
            }

            if (!objItem.Selectable)
            {
                //draw an unselectable item
                e.Graphics.DrawString(objItem.Text, _myFont2, Brushes.LightSlateGray, e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top);
            }
            else
            {
                //draw a regular item
                e.Graphics.DrawString(objItem.Text, _myFont, Brushes.Black, e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top);
            }
        }
        else
            e.Graphics.DrawString(ComboBox1.Items[e.Index].ToString(), _myFont, new SolidBrush(e.ForeColor), e.Bounds.Left + imageList1.ImageSize.Width, e.Bounds.Top);
    }
}

The magic here is done by using the Draw method of the ImageList control. This method will draw the icon in the dropdown of the combo.

Conclusion

This control is very basic out-of-the-box and most developer will use it as-is. But as I showed you once again, it is easy to extend that control to better fit your needs.


(Print this page)