.NET Compact Framework Graphics
- An Introduction to .NET Compact Framework Graphics
- Drawing on the Display Screen
- Raster Graphics
- Vector Graphics
- CONCLUSION
This chapter introduces the basics of creating graphical output from .NET Compact Framework programs.
This chapter describes the support that the .NET Compact Framework provides programs for creating graphical output. As we mention elsewhere in this book, we prefer using .NET Compact Framework classes whenever possible. To accomplish something beyond what the .NET Compact Framework supports, however, we drill through the managed-code layer to the underlying Win32 API substrate. This chapter and the two that follow discuss the .NET Compact Framework's built-in support for creating graphical output; these chapters also touch on limitations of that support and how to supplement that support with the help of GDI functions.
An Introduction to .NET Compact Framework Graphics
In general, programs do not create graphical output by drawing directly to device hardware.1 A program typically calls a library of graphical output functions. Those drawing functions, in turn, rely on device drivers that provide the device-specific elements needed to create output on a device. Historically, creating output on a graphic device such as a display screen or a printer involves these software layers:
-
Drawing program
-
Graphic function library
-
Graphic device driver (display driver or printer driver)
The core graphics library on desktop Windows is the Graphics Device Interface (GDI, gdi32.dll). With the coming of .NET, Microsoft added a second library (GDI+, gdiplus.dll 2 ) to supplement GDI drawing support. This second library provides a set of enhancements on top of the core GDI drawing functions. While the primary role for GDI+ was to support graphics for the managed-code library, it also provides a nice bonus for native-mode application programmers: the library can be called from unmanaged (native-mode) C++ programs. On the desktop, these two graphic librariesGDI and GDI+provide the underpinnings for all of the .NET graphic classes. And so, with .NET Framework programs running on the Windows desktop, the architecture of graphical output involves the following elements:
-
Managed-code program
-
Shared managed-code library (System.Drawing.dll)
-
GDI+ native-code library (gdiplus.dll)
-
GDI native-code library (gdi32.dll)
-
Graphic device driver (display driver or printer driver)
Windows CE supports a select set of GDI drawing functions. There is no library explicitly named GDI in Windows CE. Instead, the graphical output functions reside in the coredll.dll library. These functions are exactly like their desktop counterparts, so even if there is no library named GDI in Windows CE, we refer to these functions as GDI functions.
.NET Framework Drawing and Desktop Graphic Device Drivers
With the introduction of the .NET Framework, no changes were required to the graphic device drivers of any version of Microsoft Windows. That is, the device driver model used by both display screens and printer drivers was robust enough to support the .NET drawing classes.
Of the 400 or so functions that exist on desktop versions of GDI, only about 85 are included in Windows CE. Windows CE has none of the drawing functions from the extended desktop graphics library, GDI+. This places some limits on the extent to which Windows CE can support .NET drawing functions.
With just 85 of the graphical functions from the desktop's GDI library and none of the functions from GDI+, you might wonder whether Windows CE has enough graphics support to create interesting graphical output. The answer is a resounding: Yes! While there are not a lot of graphical functions, the ones that are present were hand-picked as the ones that programs tend to use most. For example, there is a good set of text, raster, and vector functions. A program can use fonts to create rich text output, display bitmaps along with other kinds of raster data (like JPEG files), and draw vector objects such as lines and polygons.
For graphical output, .NET Compact Framework programs rely on System.Drawing.dll, which is also the name of the graphical output library in the desktop .NET Framework. At 38K, the .NET Compact Framework library is significantly smaller than the 456K of its counterpart on the desktop. While the desktop library supports five namespaces, the .NET Compact Framework version supports one: System.Drawing (plus tiny fragments of two other namespaces). The architecture for drawing from a .NET Compact Framework program is as follows:
-
Managed-code program
-
Managed-code library (System.Drawing.dll)
-
GDI functions in the native-code library (coredll.dll)
-
Graphic device driver (display or printer)
From the arrangement of these software layers, a savvy .NET Compact Framework programmer can divine two interesting points: (1) The managed-code library depends on the built-in GDI drawing functions, and managed-code programs can do the same; and (2) as on the desktop, display screens and printers require a dedicated graphic driver to operate.
If Possible, Delegate Graphical Output to a Control
Before you dig into .NET Compact Framework graphics, ask yourself whether you want to create the graphical output yourself or can delegate that work to a control. If a control exists that can create the output you require, you can save yourself a lot of effort by using that control instead of writing the drawing code yourself. For example, the PictureBox control displays bitmaps and JPEG images with little effort. Aside from that single control, however, most controls are text-oriented.
Doing your own drawingand making it look goodtakes time and energy. By delegating graphical output to controls, you can concentrate on application-specific work. The built-in controls support a highly interactive, if somewhat text-oriented, user interface.
Sometimes, however, you do your own drawing to give your program a unique look and feel. In that case, you can create rich, graphical output by using classes in the .NET Compact Framework's System.Drawing namespace.
Drawing Surfaces
On the Windows desktop, there are four types of drawing surfaces:
-
Display screens
-
Printers
-
Bitmaps
-
Metafiles
When we use the term drawing surface, we mean either a physical drawing surface or a logical drawing surface. Two of the four drawing surfaces in the list are physical drawing surfaces, which require dedicated device drivers: display screens and printers. The other two drawing surfaces are logical drawing surfaces: bitmaps and metafiles. These latter two store pictures for eventual output to a device.
Bitmaps and metafiles are similar enough that they share a common base class in the desktop .NET Framework: the Image3 class. Metafiles are not officially supported in Windows CE, however, and so their wrapper, the Metafile4 class, does not exist in the current version of the .NET Compact Framework. Because metafiles might someday be supported in a future version of the .NET Compact Framework, they are worth a brief mention here.
Display Screens
The display screen plays a central role in all GUI environments because it is on the display screen that a user interacts with the various GUI applications. The real stars of the display screen are the windows after which the operating system gets its name. A window acts as a virtual console5 for interacting with a user. The physical console for a desktop PC consists of a display screen, a mouse, and a keyboard. On a Pocket PC, the physical console is made up of a display screen, a stylus and a touch-sensitive screen for pointing, and hardware buttons for input (supported, of course, by the on-screen keyboard).
All graphical output on the display screen is directed to one window or another. Enforcement of window boundaries relies on clipping. Clipping is the establishment and enforcement of drawing boundaries; a program can draw inside clipping boundaries but not outside them. The simplest clipping boundaries are a rectangle. The area inside a window where a program may draw is referred to as the window's client area.
Printers
Printers are the best-established and most-connected peripherals in the world of computers. While some industry pundits still rant about the soon-to-arrive paperless office, just the opposite has occurred. Demand for printed output has continued to go up, not down. Perhaps the world of computerswith its flashing LCD displays, volatile RAM, and ever-shrinking siliconmakes a person want something that is more real.
Printing from Windows CEpowered devices is still in its infancy, which is a nice way to say that this part of the operating system is less feature-rich than other portions. Why is that? The official story is that there is not a good enough business case for adding better printing support, meaning that users have not asked for it. The fundamental question, then, is "Why haven't users asked for better printing for Windows CE?" Perhaps it is because users are used to printing from desktop PCs. Or perhaps the problem stems from the lack of printing support in programs bundled with Pocket PCs (like Pocket Word and Pocket Excel). Whatever the cause, we show you several ways to print in Chapter 17 so that you can decide whether the results are worth the effort.
Bitmaps
Bitmaps provide a way to store a picture. Like its desktop counterparts, Windows CE supports device-independent bitmaps (DIBs) as first-class citizens. In-memory bitmaps can be created of any size6 and treated like any other drawing surface. After a program has drawn to a bitmap, that image can be put on the display screen.
If you look closely, you can see that Windows CE and the .NET Compact Framework support other raster formats. Supported formats include GIF, PNG, and JPEG. When Visual Studio .NET reads files with these formats (which it uses for inclusion in image lists, for example), it converts the raster data to a bitmap. The same occurs when a PNG or JPEG file is read from the object store into a .NET Compact Framework program. Whatever external format is used for raster data, Windows CE prefers bitmaps. In this chapter, we show how to create a bitmap from a variety of sources and how to draw those bitmaps onto the display screen from a .NET Compact Framework program.
Compressed Raster Support on Custom Windows CE Platforms
Pocket PCs support the compressed raster formats, that is, GIF, PNG, and JPEG files. Custom Windows CE platforms must include the image decompression library, named imgdecmp.dll, to receive that same support.
Metafiles
A second picture-storing mechanism supported by desktop Windows consists of metafiles. A metafile is a record-and-playback mechanism that stores the details of GDI drawing calls. The 32-bit version of Windows metafiles are known as Enhanced Metafiles (EMFs). The following Win32 native metafile functions are exported from coredll.dll but are not officially supported in Windows CE, although they might gain official support in some future version of Windows CE:
-
CreateEnhMetaFile
-
PlayEnhMetaFile
-
CloseEnhMetaFile
-
DeleteEnhMetaFile
Supported Drawing Surfaces
Of these four types of drawing surfaces, three have official support in Windows CE: display screens, printers, and bitmaps. Only two are supported by the .NET Compact Framework: display screens and bitmaps. Support for bitmaps centers around the Bitmap7 class, which we discuss later in this chapter. We start this discussion of graphical output with the drawing surface that is the focus in all GUI systems: the display screen.
Drawing Function Families
All of the graphical output functions can be organized into one of three drawing function families:
-
Text
-
Raster
-
Vector
Each family has its own set of drawing attributes and its own logic for how its drawing is done. The distinction between these three kinds of output extends from the drawing program into the graphic device drivers. Each family is complex enough for a programmer to spend many years mastering the details and intricacies of each type of drawing. The drawing support is rich enough, however, so that you do not have to be an expert to take advantage of what is offered.
Text Output
For drawing text, the most important issue involves selection of the font because all text drawing requires a font, and the font choice has the greatest impact on the visual display of text. The only other drawing attribute that affects text drawing is colorboth the foreground text and the color of the background area. We touch on text briefly in this chapter, but the topic is important enough to warrant a complete chapter, which we provide in Chapter 16.
Raster Output
Raster data involves working with arrays of pixels, sometimes known as bitmaps or image data. Internally, raster data is stored as a DIB. As we discuss in detail later in this chapter, six basic DIB formats are supported in the various versions of Windows: 1, 4, 8, 16, 24, and 32 bits per pixel. Windows CE adds a seventh DIB format to this set: 2 bits per pixel.
Windows CE provides very good support for raster data. You can dynamically create bitmaps, draw on bitmaps, display them for the user to see, and store them on disk. A bitmap, in fact, has the same rights and privileges as the display screen. By this we mean that you use the same set of drawing functions both for the screen and for bitmaps. This means you can use bitmaps to achieve interesting effects by first drawing to a bitmap and subsequently copying that image to the display screen. An important difference from desktop versions of Windows is that Windows CE does not support any type of coordinate transformations, and in particular there is no support for the rotation of bitmaps; the .NET Compact Framework inherits these limitations because it relies on native Win32 API functions for all of its graphics support.
Vector Output
Vector drawing involves drawing geometric figures like ellipses, rectangles, and polygons. There are, in fact, two sets of drawing functions for each type of figure. One set draws the border of geometric figures with a pen. The other set of functions fill the interiors of geometric figures using a brush. You'll find more details on vector drawing later in this chapter.
.NET Compact Framework Graphics
The .NET Framework has six namespaces that support the various graphical output classes. In the .NET Compact Framework, just one namespace has made the cut: System.Drawing. This namespace and its various classes are packaged in the System.Drawing.dll assembly. For a detailed comparison between the graphics support in the .NET Framework and in the .NET Compact Framework, see the sidebar titled Comparing Supported Desktop and Smart-Device Drawing.
Comparing Supported Desktop and Smart-Device Drawing
The System.Drawing namespace in the .NET Compact Framework holds the primary elements used to draw on a device screen from managed code. The desktop .NET Framework provides five namespaces for creating graphical output, but in the .NET Compact Framework this has been pared back to two: System.Drawing and System.Drawing.Design (plus some fragments from two other namespaces).
Table 15.1 summarizes the .NET namespaces supported in the desktop .NET Framework, along with details of how these features are supported in the .NET Compact Framework. The System.Drawing namespace supports drawing on a device screen. A second namespace, System.Drawing.Design, helps when building a custom control. In particular, this namespace contains elements used to support design-time drawing of controls (i.e., drawing controls while they are being laid out inside the Designer). The elements of this namespace reside in the System.CF.Design.dll assembly, a different name from the assembly name used for the desktop. The change in the file name makes it clear that this file supports .NET Compact Framework programming.
On the surface, it would be easy to conclude that Microsoft gutted the desktop System.Drawing.dll library in creating the .NET Compact Framework edition. For one thing, the desktop version is a whopping 456K, while the compact version is a scant 38K. What's more, the desktop version supports 159 classes, while the compact version has a mere 17 classes. A more specific example of the difference between the desktop .NET Framework and the .NET Compact Frameworkfrom a drawing perspectiveis best appreciated by examining the Graphics class (a member of the System.Drawing namespace). The desktop .NET Framework version of this class supports 244 methods and 18 properties; the .NET Compact Framework version supports only 26 methods and 2 properties. By this accounting, it appears that the prognosis of "gutted" is correct. Yet, as any thinking person knows, looks can be deceiving.
To understand better the difference between the desktop .NET Framework and the .NET Compact Framework, we have to dig deeper into the Graphics class. To really see the differences between the desktop and compact versions, we must study the overloaded methods. If we do, we see that the desktop .NET Framework provides many overloaded methods for each drawing call, while the .NET Compact Framework provides far fewer. For example, the desktop .NET Framework provides six different ways to call DrawString (the text drawing function), while there is only one in the .NET Compact Framework. And there are 34 versions of DrawImage (the function for drawing a bitmap) but only four in the .NET Compact Framework.
Table 15.1. Desktop .NET Framework Drawing Namespaces in the .NET Compact Framework
Namespace |
Description |
Support in the .NET Compact Framework |
---|---|---|
System.Drawing |
Core drawing objects, data structures, and functions |
A minimal set that allows for the drawing of text, raster, and vector objects with no built-in coordinate transformation |
System.Drawing.Design |
Support for the Designer and the various graphic editors of Visual Studio .NET |
Support provided by a .NET Compact Frameworkspecific alternative library named System.CF. Design.dll |
System.Drawing.Drawing2D |
Support for advanced graphic features including blends, line caps, line joins, paths, coordinate transforms, and regions |
Not supported in the .NET Compact Framework (except for the CombineMode enumeration) |
System.Drawing.Imaging |
Support for storage of pictures in metafiles and bitmaps; bitmap conversion; and management of metadata in image files |
Not supported in the .NET Compact Framework (except for the ImageAttributes class) |
System.Drawing.Printing |
Rich support for printing and the user interface for printing |
Not supported in the .NET Compact Framework |
System.Drawing.Text |
Font management |
Not supported in the .NET Compact Framework |
We have, in short, fewer ways to draw objectsbut in general we can draw most of the same things with the .NET Compact Framework that we can draw on the desktop. This supports a central design goal of Windows CE, which is to be a small, compact operating system. Win32 programmers who have worked in Windows CE will recognize that a similar trimming has been done to define the Windows CE support for the Win32 API. Instead of calling this a "subset," we prefer to take a cue from the music recording industry and use the term "greatest hits." The .NET Compact Framework implementation of the System.Drawing namespace is, we believe, the greatest hits of the desktop System.Drawing namespace.
In comparing the desktop .NET Framework to the .NET Compact Framework, an interesting pattern emerges that involves floating-point numbers. In the desktop .NET Framework, most of the overloaded methods take floating-point coordinates. For all of the overloaded versions of the DrawString methods, you can only use floating-point coordinates. In the .NET Compact Framework, few drawing functions have floating-point parametersmost take either int32 or a Rectangle to specify drawing coordinates. A notable exception is the DrawString function, which never takes integer coordinates in the desktop .NET Framework; in the .NET Compact Framework, it is the sole drawing method that accepts floating-point values.
It is worth noting that the underlying drawing functions (both in the operating system and at the device driver level) exclusively use integer coordinates. The reason is more an accident of history than anything else. The Win32 API and its supporting operating systems trace their origins back to the late 1980s, when the majority of systems did not have built-in floating-point hardware. Such support is taken for granted today, which is no doubt why the .NET Framework has such rich support for floating-point values.
A fundamental part of any graphics software is the coordinate system used to specify the location of objects drawn on a drawing surface. The desktop .NET Framework supports seven distinct drawing coordinate systems in the GraphicsUnit enumeration. Among the supported coordinates systems are Pixel, Inch, and Millimeter. While the .NET Compact Framework supports this same enumeration, it has only one member: Pixel. This means that when you draw on a device screen, you are limited to using pixel coordinates. One exception involves fonts, whose height is always specified in Point units.
This brings up another difference between the desktop .NET Framework and the .NET Compact Framework: available coordinate transformations. The desktop provides a rich set of coordinate transformationsscrolling, scaling, and rotatingthrough the Matrix class and the 3 × 3 geometric transform provided in the System.Drawing.Drawing2D namespace. The .NET Compact Framework, by contrast, supports no coordinate mapping. That means that, on handheld devices, application software that wants to scale, scroll, or rotate must handle the arithmetic itself because neither the .NET Compact Framework nor the underlying operating system provides any coordinate transformation helpers. What the .NET Compact Framework provides, as far as coordinates go, is actually the same thing that the underlying Windows CE system provides: pixels, more pixels, and only pixels.
While it might be lean, the set of drawing services provided in the .NET Compact Framework is surprisingly complete. That is, almost anything you can draw with the desktop .NET Framework can be drawn with the .NET Compact Framework. The key difference between the two implementations is that the desktop provides a far wider array of tools and helpers for drawing. Programmers of the desktop .NET Framework are likely to have little trouble getting comfortable in the .NET Compact Framework, once they get used to the fact that there are far fewer features. But those same programmers are likely to be a bit frustrated when porting desktop .NET Framework code to the .NET Compact Framework world and are likely to have to rewrite and retrofit quite a few of their applications' drawing elements.
The Role of the Graphics Class
The most important class for creating graphical output is the Graphics8 class. It is not the only class in the System.Drawing namespace, but only the Graphics class has drawing methods. This class holds methods like DrawString for drawing a string of text, DrawImage for displaying a bitmap onto the display screen,9 and DrawRectangle for drawing the outline of a rectangle. Here is a list of the other classes in the System.Drawing namespace for the .NET Compact Framework:
-
Bitmap
-
Brush
-
Color
-
Font
-
FontFamily
-
Icon
-
Image
-
Pen
-
Region
-
SolidBrush
-
SystemColors
These other classes support objects that aid in the creation of graphical output, but none has any methods that actually cause graphical output to appear anywhere. So while you are going to need these other classes and will use these other classes, they play a secondary role to the primary graphical output class in the .NET Compact Framework: Graphics.
Drawing Support for Text Output
Table 15.2 summarizes the methods of the Graphics class that support text drawing. The DrawString method draws text, while the MeasureString method calculates the bounding box of a text string. This calculation is needed because graphical output involves putting different types of graphical objects on a sea of pixels. When dealing with a lot of text, it is important to measure the size of each textbox to make sure that the spacing matches the spacing as defined by the font designer. Failure to use proper spacing creates a poor result. In the worst cases, it makes the output of your program unattractive to users. Even if a user does not immediately notice minor spacing problems, the human eye is very finicky about what text it considers acceptable. Poor spacing makes text harder to read because readers must strain their eyes to read the text. Properly spaced text makes readersand their eyeshappier than poorly spaced text does.
Drawing Support for Raster Output
Table 15.3 summarizes the methods of the Graphics class that draw raster data. We define raster graphics as those functions that operate on an array of pixels. Two of the listed functions copy an icon (DrawIcon) or a bitmap (DrawImage) to a drawing surface. The other two methods fill a rectangular area with the color of an indicated brush. We discuss the details of creating and drawing with bitmaps later in this chapter.
Drawing Support for Vector Output
Table 15.4 summarizes the seven methods in the Graphics class that draw vector Graphics objects in the .NET Compact Framework. There are substantially fewer supported vector methods than in the desktop .NET Framework. The vector methods whose names start with Draw draw lines. The vector methods whose names start with Fill fill areas.
Table 15.2. System.Drawing.Graphics Methods for Text Drawing
Method |
Comment |
---|---|
DrawString |
Draws a single line of text using a specified font and text color. |
MeasureString |
Calculates the width and height of a specific character string using a specific font. |
Table 15.3. System.Drawing.Graphics Methods for Raster Drawing
Method |
Comment |
---|---|
Clear |
Accepts a color value and uses that value to fill the entire surface of a window or the entire surface of a bitmap. |
DrawIcon |
Draws an icon at a specified location. An icon is a raster image created from two rectangular bitmap masks. The DrawIcon method draws an icon by applying one of the masks to the drawing surface using a Boolean AND operator, followed by the use of the XOR operator to apply the second mask to the drawing surface. The benefit of icons is that they allow portions of an otherwise rectangular image to display the screen behind the icon. The disadvantage of icons is that they are larger than comparable bitmaps and also slower to draw. |
DrawImage |
Draws a bitmap onto the display screen or draws a bitmap onto the surface of another bitmap. |
FillRegion |
Fills a region with the color specified in a brush. A region is defined as a set of one or more rectangles joined by Boolean operations. |
Table 15.4. System.Drawing.Graphics Methods for Vector Drawing
Method |
Comment |
---|---|
DrawEllipse |
Draws the outline of an ellipse using a pen. |
DrawLine |
Draws a straight line using a pen. |
DrawPolygon |
Draws the outline of a polygon using a pen. |
DrawRectangle |
Draws the outline of a rectangle using a pen. |
FillEllipse |
Fills the interior of an ellipse using a brush. |
FillPolygon |
Fills the interior of a polygon using a brush. |
FillRectangle |
Fills the interior of a rectangle using a brush. |