The new Windows icons versus Visual Basic icons
In the Windows interface, the size of shell icons is determined by the user. Try it. Right-click on the desktop and choose Properties to bring up the Display Properties dialog box. Select the Appearance tab, click the Item combo box, and select Icon. Chances are you’ll see the default icon size, 32. Change it to 37 or 24, and click Apply. All the icons on your desktop will change to the new size. If you move to Windows Explorer and set Large Icons mode, you’ll see the same size. This feature is part of the new philosophy of allowing users to customize more parts of the desktop. Users with visual disabilities can make their icons as large as they want. Those who want to cram as much stuff as they can onto their screens, visibility be damned, can have small icons everywhere.
So what happens when you perform similar operations in Visual Basic? The Test Icons program (TICON.VBP) shown in Figure 8-5 illustrates. The sample program displays icons on a background of clouds so that you can tell that they’re really icons and not just bitmaps drawn on a solid background. Of course, this doesn’t do any good on the PictureBox control because it doesn’t have a transparent mode. In real life, you probably won’t put icons in the PictureBox Picture property (although you could draw them transparently on the Picture with DrawIconEx).
In order to make image differences more visual, I used a modified dual-image icon (I.ICO) that has a blue L in the large icon image and a pink S in the small image. (Don’t try this at home.) When I captured the screen shot, my desktop icon size was 24 by 24 pixels, the size that showed up in a shortcut to the Test Icons program (also shown in Figure 8-5 for comparison). In this example, the Image controls have Stretch set to False and the PictureBox controls have AutoSize set to True. The Basic icon is loaded with Visual Basic’s LoadPicture. The rest are loaded with my LoadAnyPicture.
Figure 8-5. Icons for every occasion.
-
The large (Default) icon comes out at 32 by 32 pixels whether you get it from Visual Basic’s LoadPicture function or from my LoadAnyPicture function. This is, alas, the system icon size (from GetSystemMetrics) of my monitor. My boss turned down my request for a 48-inch monitor with a 4096 by 4096 pixel display and 48 by 48 pixel icons. In fact, I’ve never seen a display adapter with a system icon size other than 32, so I’m not sure what Visual Basic would do with one.
-
The Shell Size icon comes out the right size in both the Image and the PictureBox controls. If you look closely, you’ll see some jaggies on the icon edge, indicating that this image has been compressed from its real image size (32 by 32 pixels).
-
The Image Size icon comes out to the actual size of the large icon. But since the icon file contains both a 32 by 32 pixel and a 16 by 16 pixel image, how did the program know which one to use? I’ll get to that.
-
The small and huge icons display correctly in both controls. The small icon gets the small image. The huge icon uses the large image, but stretches it out to a 48 by 48 frame. Notice the jaggies from stretching the icon image.
You have to do some extra work to get the appropriate image, but at least the controls do the right thing if you give them the right stuff. This is a marked improvement over Visual Basic version 4, which did a particularly poor job of displaying non-standard icons in PictureBox controls.
Visual Basic and the Picture object just don’t have a Windows state of mind about icons. Windows programs are supposed to display large icons with code like this:
cx = GetSystemMetrics(CX_ICON)
cy = GetSystemMetrics(CY_ICON)
f = DrawIconEx(hdc, x, y, hIcon, cx, cy, 0, NULL, DI_NORMAL)
I imagine that under the surface, DrawIconEx calls StretchBlt to stretch the image and mask bitmaps of the icon from their actual size to the desired icon size. Small icons work the same way except that they must stretch the actual icon bitmaps down to the hard-coded size of 16 by 16 pixels. Shell size icons get the user-selected size using a technique similar to that found in my GetShellIconSize function. You can see the code in PICTOOL.BAS.
Often, when Windows uses multiple-image icon files or resources, it doesn’t need to do any stretching. It can simply blast a 16 by 16 pixel image onto a small icon or a 32 by 32 pixel image onto a large icon with the default size. If the target icon size doesn’t match any of the images in the file or resource, Windows probably has an algorithm for deciding which image will best stretch into the target size.
This works out pretty well for C programmers because they load icons from resources or files, which can contain multiple images. Visual Basic programmers load icons from Picture objects, which can contain only a single image. Worse yet, none of the ways to load an icon into a Picture object gives you any choice about which image to load. For example, the Visual Basic LoadPicture function and the Load Picture dialog box always load the system metrics size. If the user keeps the default icon size (usually 32 by 32 pixels), LoadPicture always loads a 32 by 32 pixel icon. If the icon file being loaded contains a single 16 by 16 pixel image, LoadPicture stretches it to 32 by 32 pixels before storing it in the Picture object. A small icon puffed into a large icon container looks just as bad as a large icon crammed into a small icon container.
The only thing Visual Basic does right with dual-image icons is display them correctly in the Icon property. Small icons appear correctly in the Taskbar and in the left corner of the application’s title bar. Large icons are displayed correctly if they are placed on the desktop. This is the most pervasive use of large and small icons, so it’s fortunate that it works. But you can do lots of other things with icons—things that hardcore programmers won’t give up on easily just because Visual Basic doesn’t help them.