System Colors and Sizes
What was the first difference that jumped out at you the first time you loaded Visual Basic under Windows 95 (assuming for the moment that you were experienced with Visual Basic version 3)? Chances are you said something like, “Holy Hypotenuse, Batman! They’ve changed the default color of forms. Windows has adopted bat colors—yellow with a black title bar.” Or perhaps not. Actually, most people saw gray forms with a blue title bar. But whatever color you saw, you didn’t really see it, because, by default, forms don’t have a color.
The default BackColor property of forms is vb3DFace, a constant that evaluates to &H8000000F. (Look it up on the System tab of any color property.) Color values are made up of red, green, and blue elements in the first 3 bytes of a Long, but this number has &H80 in the fourth byte. In other words, it’s not a valid RGB color; it’s a constant that represents a system color. In cruder languages such as C++, you must call an API function:
BackColor = GetSysColor(COLOR_3DFACE)
Visual Basic does this for you when you write this:
BackColor = vb3DFace
It’s your responsibility to use the system constants faithfully rather than hard-coding specific colors. If your user creates a Batman color scheme with the Display Properties dialog box, who are you to argue? Obviously, there are exceptions. If you’re writing a hospital application and decide that it’s important to make your forms a sickly green color, you might decide to consciously override a user’s color choices with your own.
It’s important to understand the difference between the system colors you assign to Visual Basic properties and the RGB colors you pass to most GDI functions. For reasons known only to historians, system colors have the type OLE_COLOR. If you give a property this type in a UserControl, it will display the standard color picker in the Properties window. If you make the property type Long, it will be just another integer property. On the other hand, if you pass a system color to an API function that expects an RGB color, you probably won’t like the result. To complicate things further, there’s a third kind of color—an index into the current color palette. You probably won’t find much use for these colors unless you go far beyond the meager palette techniques discussed in “Using Bitmaps” in Chapter 8.
If you have a system color but want an RGB color, you can use the OleTranslateColor function to find the real color behind the color. This is one of the most hideously complicated simple functions in the API. My TranslateColor wrapper function makes it easy to do common operations like this:
rgbButtonFace = TranslateColor(vbButtonFace)
Here’s the code that makes it work:
Function TranslateColor(ByVal clr As OLE_COLOR, _
Optional hPal As Long = 0) As Long
If OleTranslateColor(clr, hPal, TranslateColor) Then
TranslateColor = CLR_INVALID
End If
End Function
If you’re wondering what the hPal parameter is or what OleTranslateColor returns, check it out in the API documentation.
Under the new Windows interface, the user can change a lot besides colors. On the Appearance tab of the Display Properties dialog box, for instance, you can change not only colors but also sizes for many elements. So if you’re a hardcore programmer whose programs use the size of window elements, such as the height of the title bar or the width of a scroll bar, make sure that you get the true size of these elements. The same goes for fonts. The user can change almost any font used in menus, windows, or other standard elements. Information about your system is available from one of two places. You can get system colors and many other system parameters by using GetSystemMetrics. Values specific to the user interface are located in the registry, specifically in the \Control Panel\Desktop\WindowMetrics key of HKEY_CURRENT_USER.
It’s better not to read them directly if you can find an alternative, however, because the registry isn’t guaranteed to always be in sync with the Windows desktop.