The Draw method and the DrawImage function


The Draw method allows you to draw an image from an ImageList directly onto a form, a picture box, or anything else that has an hDC property. This method does, however, illustrate a flaw in the ImageList design, one that belies the purpose of the underlying ImageList API functions.


You call the Draw method as shown here:

imlstBmps.ListImages(iBmps).Draw pb.hDC, x, y, imlNormal

The final parameter is an Enum of type ImageDrawConstants containing the constants imlNormal, imlTransparent, imlSelected, or imlFocus. The latter two specify that an image should be dithered to different degrees to give a shaded effect. The values of these constants are 0 through 3. You select one and only one. But what if you want to draw a transparent selected image? In fact, transparent is the only way I’d ever want to draw an image as selected or with focus.


Let’s look at the API functions behind the Draw method. An image list is created with the ImageList_ family of API functions. The Draw method uses either the ImageList_Draw function or its enhanced cousin, ImageList­_DrawEx. To keep things simple, I’ll use ImageList_Draw, which is called this way:

f = ImageList_Draw(hImageList, i, hdc, x, y, _
ILD_TRANSPARENT Or ILD_SELECTED)

Notice the final style argument. It’s obviously a bitfield, not an enumeration. The values of the ILD_ constants confirm this; they’re &H0, &H1, &H2, &H4, &H10—not 0, 1, 2, 3. Furthermore, there are five of them, not four. ILD_MASK draws a monochrome mask of the image, but you won’t find a parallel imlMask constant. Perhaps the designer of the ImageList control didn’t know why a Visual Basic programmer would want a mask. But you do (if you read Chapter 7). The constant ILD_NORMAL has the value 0, which indicates the absence of any modifying bits rather than a separate kind of image.


Hardcore programmers have to forget the ImageList Draw method and go down to the API to get what they need. When using the hImageList property, keep in mind that the default scale mode of the API is pixels, not twips, and that API indexes are zero-based, not one-based. Here’s a DrawImage function that replaces the Draw method:

Sub DrawImage(imlst As Control, vIndex As Variant, ByVal hDC As Long, _
ByVal x As Long, ByVal y As Long, _
Optional ByVal afDraw As EILD = ILD_TRANSPARENT)
#End If
ImageList_Draw imlst.hImageList, _
imlst.ListImages(vIndex).Index - 1, hDC, _
x / Screen.TwipsPerPixelX, _
y / Screen.TwipsPerPixelY, afDraw
End Sub

This version is hard-coded to assume twips. Notice that it adjusts the one-based ImageList index. By default, it draws transparent images.


ImageList Challenge


The ImageList API functions have other features that aren’t passed through in the ImageList control. Here are some ideas you might try, using the declarations, constants, and functions in COMCTL.BAS and the Windows API type library. Look at the extra arguments of ImageList_DrawEx. You can stretch an image to any size and control its background and foreground colors. Unfortunately, you can’t use ROP arguments to blit images as you can with the PaintPicture method or the BitBlt function. The ImageList_ExtractIcon and ImageList_GetIcon functions give you a handle to an icon. If you carefully study the earlier section on icons, you can come up with a way to get the transparency of an icon without stretching it to the standard icon size (even if you have inserted a bitmap). Use the handle to draw directly with the DrawIconEx API function.

It wouldn’t be difficult to create a new ActiveX control that fixes many of the ImageList control’s limitations. For that matter, you could totally replace the ImageList with your own implementation using the API. Since ImageList doesn’t have events, there’s a good case for making it an ActiveX DLL component rather than a control anyway. Of course, you wouldn’t be able to initialize the images of an ImageList server at design time, but many programs could live with that limitation. Unfortunately, other controls that require ImageList controls probably wouldn’t be able to use your enhanced component.

Normally, you use an ImageList like a roll of snapshots that you can access by name or by number. But if you arrange these snapshots in sequential order, like movie frames, you could generate a moving picture just by playing them in sequence. It wouldn’t be too difficult to build an animation class based on an ImageList control.


I would rather have designed this function to take a ListImage object instead of an ImageList control and an index, calling it like this:

DrawImage imlstIcons.ListImage(iIcons), hDC, x, y

But the hImageList property is on ImageList, and ListImage has no Parent property to refer back to the ImageList. I must supply an extra argument:

DrawImage imlstIcons, iIcons, hDC, x, y

You can use similar techniques to get around limitations of other common controls. ImageList is invisible and has its own special handle and API functions. Other controls have an hWnd property that you can use to send messages to their windows.