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.