Introduction
ImageComboBox
is an extension of the standard Windows ComboBox
control, with support for displaying icons or images along with the item text. If you take a look at the Windows interface, you can see the Windows Explorer, Internet Explorer, File Open/Save/Print Dialogs, all using a combobox which can show icons and different levels of indentation.
A standard combobox does not support displaying graphic images, in the Normal DrawMode
. But it can display icons or images when it is OwnerDrawn
. Still there is something missing. The images can be drawn in the list portion, but when you select an item, its image is not displayed in the textbox except when the dropdown style is set to DropDownList
. The combobox does not support editing when the dropdown style is DropDownList
. The editable modes are Simple
and DropDown
. So I wanted the image to be drawn in the textbox, in all three DropDownStyle
s. This ImageComboBox
is able to draw items along with the corresponding images in the list portion and text portion in all three DropDownStyle
s.
Apart from that this ImageComboBox
also supports multiple levels of indentation for individual items. Indenting individual items enhances visual representation by grouping items and showing hierarchical relationships. The individual items can have their own font and size specified, when the DrawMode
is set to OwnerDrawVariable
. So an item in the combobox has several properties and all these properties need to be manipulated during design time or dynamically at runtime. Therefore the items are no longer simple strings, instead they are distinct ImageComboBoxItem
objects. So the Items
is a collection of ImageComboBoxItem
objects.
ImageComboBoxItem
The ImageComboBoxItem
has the following properties:
Image
The image to be displayed along with the combobox item. The Image
property exposes a dropdown window which shows the images taken from the ImageList
.
Font
The font to be used for the combobox item text, when the DrawMode
is set to OwnerDrawVariable
. The Font
property exposes a font editor to select the desired font.
IndentLevel
The level of indentation needed for an item. The ImageComboBox
supports up to four levels of indentation, i.e., 0 -5.
Text
The text of the combobox item.
The ImageComboBox
also needs two new properties, an ImageList
to hold the icons/images from which the user can select images associated with an item, and an Indent
property to let the user specify the amount of indentation. The Ite
ms property is changed so that the items are of type ImageComboBoxItemCollection
. A collection editor is provided to the user so that the properties of individual items can be added/removed/modified at design time.
ImageComboBox
Following properties are newly added to the ImageComboBox
:
ImageList
Holds the collection of images to be drawn with the items.
Items
(changed property)
The items of ImageComboBox
. The items are of type ImageComboBoxItemCollection
. The Items
property exposes a new collection editor to add/remove/edit combobox items.
Indent
The amount of indentation needed in pixels.
How to draw an image in the Edit portion of the ComboBox?
Displaying images in the combobox dropdown portion is fairly straightforward, and can be done by making the combobox owner drawn and drawing each item with an added image. On the other hand, displaying an image in the edit portion of the combobox when the DropDownStyle
is set to DropDown
or Simple
is a difficult task. It requires getting the handle of the edit box, drawing the image, and setting a margin to the editbox so that the text displayed will be indented properly.
The good old Windows API calls do the trick.
- Get the handle of the Edit Box. The
ComboBox
is a combination of a TextBox
and a ListBox
. The GetComboBoxInfo(IntPtr hwndCombo, ref ComboBoxInfo info)
method retrieves the handles and the coordinates of the TextBox
and ListBox
. [DllImport("user32")]
private static extern bool GetComboBoxInfo(IntPtr hwndCombo,
ref ComboBoxInfo info);
[StructLayout(LayoutKind.Sequential)]
private struct ComboBoxInfo
{
public int cbSize;
public RECT rcItem;
public RECT rcButton;
public IntPtr stateButton;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
- Set margin to the
TextBox
. [DllImport("user32", CharSet=CharSet.Auto)]
private extern static int SendMessage(IntPtr hwnd,
int wMsg, int wParam, int lParam);
private const int EC_LEFTMARGIN = 0x1;
private const int EC_RIGHTMARGIN = 0x2;
private const int EM_SETMARGINS = 0xD3;
Set the margins using the SendMessage
method. The combobox supports displaying items either left-to-right, or right-to-left. Depending on this setting, the margin has to be set. RightMargin
needs to be in the hi-word, so multiply by 65536.
SendMessage(ComboBox.Handle, EM_SETMARGINS,
EC_RIGHTMARGIN, Margin * 65536);
SendMessage(ComboBox.Handle, EM_SETMARGINS, EC_LEFTMARGIN, Margin);
- Drawing the image onto the text box.
To achieve this, a class derived from NativeWindow
is required. To this class we assign the handle of the TextBox
so that we get access to the message stream directed to the TextBox
. Then override the WndProc
method and repaint the TextBox
manually.
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PAINT:
base.WndProc(ref m);
DrawImage();
break;
case WM_LBUTTONDOWN:
base.WndProc(ref m);
DrawImage();
break;
case WM_KEYDOWN:
base.WndProc(ref m);
DrawImage();
break;
case WM_KEYUP:
base.WndProc(ref m);
DrawImage();
break;
case WM_CHAR:
base.WndProc(ref m);
DrawImage();
break;
case WM_GETTEXTLENGTH:
base.WndProc(ref m);
DrawImage();
break;
case WM_GETTEXT:
base.WndProc(ref m);
DrawImage();
break;
default:
base.WndProc(ref m);
break;
}
}
public void DrawImage()
{
if((CurrentIcon!=null))
{
gfx = Graphics.FromHwnd (this.Handle);
bool rightToLeft = false;
if(Owner.RightToLeft == RightToLeft.Inherit)
{
if(Owner.Parent.RightToLeft == RightToLeft.Yes)
rightToLeft = true;
}
if(Owner.RightToLeft == RightToLeft.Yes || rightToLeft)
{
gfx.DrawImage(CurrentIcon,
gfx.VisibleClipBounds.Width - CurrentIcon.Width,0);
}
else if(Owner.RightToLeft == RightToLeft.No || rightToLeft)
gfx.DrawImage(CurrentIcon,0,0);
gfx.Flush();
gfx.Dispose();
}
}
Using the ImageComboBox
Copy the ImageComoBox.dll from the bin\Release directory to your project directory. Open the Solution Explorer, right click the project name, and choose Add Reference. Click Browse button, locate the ImageComoBox.dll and select it. To display the control in Toolbox, right click on the toolbox, click Add/Remove Items. From the Customize Toolbox window, select the ImageComoBox.dll.