Device Contexts and Canvas Objects

In Windows, the surface on which you draw is called a device context (DC). You access it through a handle called an HDC. A device context handle is to graphics what a window handle is to controls and windows.

Once you have the handle to a device context, you can use API calls to put dots, lines, circles, rectangles, text, and existing pictures of various kinds on it. The device context translates your device-independent instructions about what to draw or paint into device-specific instructions for a particular output device. Almost every function in the graphics device interface (GDI) either has an HDC argument or returns an HDC. You simply can’t do much graphics work without one.

No official name exists for the corresponding concept in Visual Basic, but some people at Microsoft, who ought to know, refer to objects that have graphics capabilities as canvas objects. I’ll use that term to describe the various graphics objects. The Form, PictureBox, and Printer objects are the three standard canvas objects; Screen, Image, Shape, and Line objects also have canvaslike properties and methods.

I like to think of a CCanvas class as forming the top of a hierarchy such as the one shown in Figure 7-1. My theory is that Visual Basic objects are implemented internally using inheritance and that in a future version they’ll get around to making these classes available to us poor programming peons. I have no inside

information on this (and would deny it if I did), but imagining a canvas object hierarchy gives me a useful model of what’s going on.

In my theory, the CCanvas class has all the generic properties and methods com­mon to canvas objects, but it’s not public so you can’t create your own canvas object. It’s just something to hang your inheritance on. The CPrinter class is inherited from CCanvas and has all its generic properties and methods: Fore­Color, DrawMode, DrawStyle, ScaleWidth, ScaleHeight, Line, Circle, and so on. It adds its own printer-specific methods such as NewPage and EndDoc, which are implemented internally using API functions such as StartDoc, EndDoc, StartPage, and EndPage.

The CDisplay class is also inherited from CCanvas and is likewise invisible to users. It adds display-oriented properties and methods such as Picture, Image, Cls, and Point. The CForm and CPictureBox classes are inherited from CDisplay, and each one adds its own unique properties and methods—although most of these have nothing to do with graphics.

I tried to throw the Screen, Image, Shape, and Line classes into this hierarchy, but they just muddied my nice, clean diagram.

Now stretch your imagination a bit and imagine that the Visual Basic classes implement interfaces rather than inherit from classes. Let’s say that there’s an ICanvas interface that provides such members as hDC, Width, Left, Move, ScaleWidth, and ScaleX. Classes that implement this interface include Form, PictureBox, Printer, and UserControl. The IDrawable interface provides drawing members such as Line, Circle, PSet, DrawMode, and CurrentX. It is also implemented by Form, PictureBox, Printer, and UserControl. The IReadable interface provides the Point method. It is implemented by Form, PictureBox, and UserControl, but not by Printer. It doesn’t make sense to read the color of a point off a printed page—that’s a separate operation that could be provided by an IScanner interface. Figure 7-2 shows how an interface hierarchy might work.

I suspect that the internal Visual Basic approach combines these approaches. I don’t think Form and Printer both implement IDrawable separately. They probably inherit a common implementation. As we’ll see in this chapter, drawing a line to the printer, a form, or a PictureBox is exactly the same code, except that you use different device contexts.

We can speculate all we want about how Visual Basic is implemented, but if the features aren’t made public they might as well be implemented with black magic. If you prefer facts to theories, Table 7-1 shows a matrix of some of the more common properties and methods shared by canvas objects. Note that UserControl, UserDocument, and PropertyPage have the same properties and methods as Form and therefore are not shown separately.

Figure 7-1. The CCanvas class hierarchy.