Graphics Examples

 

Basic examples

User Interfaces

Many resource-constrained devices do not need a complex graphical display. They provide an intuitive user interface with buttons and LEDs or a simple text display. However, there is a growing demand for devices that provide a complex user interface with a detailed graphical display. The .NET Micro Framework provides built-in support for color LCD displays, if your device has one.

In this chapter, we will explore how to create graphics and graphical user interfaces with the .NET Micro Framework for presentation on an LCD display. You will first learn how to draw directly onto the display with the Bitmap class, and later in this chapter, you will learn how to create complex graphical user interfaces and interaction using a kind of trimmed Windows Presentation Foundation (WPF) for the .NET Micro Framework. This part of the chapter presents the various built-in components and describes when and how to use them. For example, you will learn how to build rich list box menus, build text and image displays, respond to user input, and implement custom user interface elements.

Since images are embedded as resources into an assembly, you should have read the section about resources in Chapter 10.

Even if you do not write directly to bitmaps, and program graphical applications using the richer presentation classes, you should be familiar with the bitmap information presented in the first part of this chapter because the presentation classes internally use bitmaps for drawing; they are also helpful in deriving custom controls.

Drawing on the Display Using the Bitmap class

The Bitmap class provides support for setting particular pixels and drawing graphical elements such as lines, rectangles, and ellipses. You can also use it to draw other images and texts with different fonts. We will explore most of these features in detail later in this chapter.

You can find the Bitmap class in the Microsoft.SPOT namespace, in the Microsoft.SPOT.

Graphics.dll assembly. To give you an overview of the possibilities, the Bitmap class is presented in Listing 11-1.

■Note The Bitmap class possesses further members that are not presented in Listing 11-1, because they are marked as obsolete. You do not need them, because they just provide other inflexible overloads. It seems that they were intended for the black and white displays of Microsoft’s Smart Personal Object Technology (SPOT) watches, since most of the obsolete methods lack a color parameter.

Listing 11-1. Bitmap Class

using Microsoft.SPOT.Presentation.Media; 

using System;

namespace Microsoft.SPOT
{
public sealed class Bitmap : MarshalByRefObject
{
// constants
public const ushort OpacityOpaque = 256;
public const ushort OpacityTransparent = 0;
// static properties
public static readonly int CenterX;
public static readonly int CenterY;
public static readonly int MaxHeight;
public static readonly int MaxWidth;
// constructors
public Bitmap(byte[] imageData, Bitmap.BitmapImageType type);
public Bitmap(int width, int height);
// determining dimensions
public int Height { get; }
public int Width { get; }
// erasing the bitmap
public void Clear();
// drawing pixels
public Color GetPixel(int xPos, int yPos);
public void SetPixel(int xPos, int yPos, Color color);
// drawing lines
public void DrawLine(Color color, int thickness,int x0, int y0, int x1, int y1);
// drawing rectangles
public void DrawRectangle(Color colorOutline, int thicknessOutline,
int x, int y, int width, int height,
int xCornerRadius, int yCornerRadius,
Color colorGradientStart,
int xGradientStart, int yGradientStart,
Color colorGradientEnd,
int xGradientEnd, int yGradientEnd,
ushort opacity);
// drawing ellipses
public void DrawEllipse(Color colorOutline,
int x, int y,
int xRadius, int yRadius);
public void DrawEllipse(Color colorOutline,
int thicknessOutline,
int x, int y,
int xRadius, int yRadius,
Color colorGradientStart,
int xGradientStart, int yGradientStart,
Color colorGradientEnd,
int xGradientEnd, int yGradientEnd,
double opacity);
// drawing other images
public void DrawImage(int xDst, int yDst,
Bitmap bitmap,
int xSrc, int ySrc, int width, int height);
public void DrawImage(int xDst, int yDst,
Bitmap bitmap,
int xSrc, int ySrc, int width, int height,
ushort opacity);
public void StretchImage(int xDst, int yDst, Bitmap bitmap,
int width, int height, ushort opacity);
// drawing text
public void DrawText(string text, Font font, Color color, int x, int y);
public void DrawTextInRect(string text, int x, int y, int width,
int height, uint dtFlags, Color color, Font font);
public bool DrawTextInRect(ref string text,
ref int xRelStart, ref int yRelStart,
int x, int y, int width, int height,
uint dtFlags, Color color, Font font);
// flushing content to display
public void Flush();
public void Flush(int x, int y, int width, int height);
// setting transparency
public void MakeTransparent(Color color);
// clipping
public void SetClippingRectangle(int x, int y, int width, int height);
public enum BitmapImageType
{
TinyCLRBitmap = 0,
Gif = 1,
Jpeg = 2,
Bmp = 3,
}
}
}

Using Bitmaps

You do not need to create a .NET Micro Framework window application to use bitmaps with a nanoFramework project. This kind of application is intended for use with the more complex but richer presentation classes. To use bitmaps, you can just create a simple console application, but you need to add a reference to the nanoFramework.Graphics library to your project.

You can easily test your applications on the sample emulator provided with the .NET Micro Framework. It has a color QVGA (320×240) display with a color depth of 16 bits per pixel. You will learn how you can configure the emulator display in the next chapter, which covers hardware emulation.

The Bitmap class provides two constructors. One creates a bitmap on the basis of specified binary image data. The other one creates an empty bitmap of a particular size. The following code creates an empty bitmap that’s the size of the display:

ScreenMetrics metrics = ScreenMetrics.GetMetrics();

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

The resolution of the display is determined at runtime with the ScreenMetrics class that resides in the Microsoft.SPOT.Hardware namespace in Microsoft.SPOT.Graphics.dll.

The color of the new bitmap is black, and you will not see it displayed yet: the bitmap is kept in memory, so you can draw on it and then display it. Graphic coordinates are specified in pixels, and the origin, with the coordinates (0, 0), is in the top-left corner. The following code will draw a white line from the top-left corner to the bottom-right corner on the bitmap: bmp.DrawLine(Microsoft.SPOT.Presentation.Media.Color.White, // color

1, // line thickness

0, 0, // start point

bmp.Width, bmp.Height); // end point

Flushing Bitmaps onto the Display

The preceding drawing operation will not display the line automatically onto your display.

Drawing is performed to the bitmap buffer in memory. You need to call the Flush method of the bitmap to show it on your display.

Calling the bmp.Flush()method will copy your entire bitmap onto the display, as shown in Figure 11-1.

Figure 11-1. Flushing the entire bitmap

There is another Flush method that allows you to copy only a part of your bitmap onto the display. Using the following code snippet instead of the previous one will copy only a rectangular part to the display, as shown in Figure 11-2:

bmp.Flush(bmp.Width / 2 - 50, //left

bmp.Height / 2 - 50, //top

100, //width

100); //height

Figure 11-2. Flushing part of a bitmap

It is also possible to flush from a different source bitmap to your screen.

■Caution In order to flush bitmaps onto your display, you need to create the source bitmap with exactly the same dimensions as your display, even if you will flush only a part to your display.

Using Colors

In the previous example, you saw how to draw a white line. Colors are represented in the .NET

Micro Framework with the Microsoft.SPOT.Presentation.Media.Color enumerated type in the Microsoft.SPOT.Graphics.dll library. This enumeration has only the members Black and White.

But you can create your own color from red, green, and blue (RGB) intensity values with the Microsoft.SPOT.Presentation.Media.ColorUtility class. This class has a static ColorFromRGB

method to create a custom color from the specified RGB intensity components. Table 11-1 lists the known colors from the full .NET Framework and their RGB intensities. You can use the RGB

values from that table with the ColorUtility.ColorFromRGB method. For example, to create a yellow color, you need to use the following code:

Color yellowColor = ColorUtility.ColorFromRGB(0xFF, 0xFF, 0x00);

Yellow is a mix of red and green. The red, green, and blue intensities are accepted as byte values ranging from 0 to 255 (0xFF in hexadecimal). A color value is actually a 32-bit integer that uses 24 bits to store the RGB value. It is possible to cast an integer directly into a color value: Color pinkColor = (Color)0xCBC0FF; // BBGGRR (R=0xFF, G=0xC0, B=0xCB)

The ColorUtility class has three helper methods—GetRValue, GetGValue, and GetBValue—

to split a color into its red, green, and blue components.

The ScreenMetrics class provides the BitsPerPixel property to determine the supported color depth of your hardware configuration.

To set the color for an individual pixel, you can use the SetPixel method of the Bitmap class. The method accepts the x and y coordinates as well as the new color value. The method GetPixel will return the color value from a particular pixel on the bitmap.

Drawing Rectangles

The Bitmap class allows you to draw rectangles. Therefore, it provides a DrawRectangle method that accepts quite a lot of parameters to achieve some impressive graphical effects.

Drawing Solid-Filled Rectangles

The DrawRectangle method class in Listing 11-2 will draw a simple solid-filled white rectangle.

In this example, no outline will be drawn, so the code uses an outline thickness of 0 pixels. The start color and end color are both set to white, which causes the rectangle to be filled entirely with the same color.

Listing 11-2. Drawing a White, Solid-Filled Rectangle

bmp.DrawRectangle(Color.White,   // outline color
0, // outline thickness
100, 100, // x and y of top left corner
200, 100, // width and height
0, 0, // x and y corner radius
Color.White, // gradient start color
0, 0, // gradient start coordinates
Color.White, // gradient end color
0, 0, // gradient end coordinates
Bitmap.OpacityOpaque); // opacity

Drawing a Rectangular Frame

To draw a rectangular frame, you need to set the opacity to Bitmap.OpacityTransparent (see Listing 11-3), which draws an unfilled rectangle with only a border with the selected border color and thickness. The border frame is always drawn fully opaque with the border color regardless of the opacity value; the opacity value only affects filling.

Listing 11-3. Drawing a Rectangular Frame

bmp.DrawRectangle( Color.White, // outline color
1, // outline thickness
100, 100, // x and y of top left corner
200, 100, // width and height
0, 0, // x and y corner radius
Color.White, // gradient start color
0, 0, // gradient start coordinates
Color.White, // gradient end color
0, 0, // gradient end coordinates
Bitmap.OpacityTransparent); // opacity

Drawing Rectangles with Opacity

You can get quite interesting effects when using the opacity parameter. Opacity values range from 0 to 256, where 0 (or the constant Bitmap.OpacityTransparent) does not draw a pixel and the value 256 (or Bitmap.OpacityOpaque) completely replaces what is underneath (the documentation for the SDK indicates that 0xFF, or 255, is opaque, which is not true). Figure 11-3

illustrates what effects are possible with reduced opacity values; the sample cascades three filled rectangles of different colors and draws the overlapping boxes in a for loop. Listing 11-4

shows the code to draw this art.

Figure 11-3. Filled overlapping boxes using reduced opacity values Listing 11-4. Drawing Filled Overlapping Boxes with Reduced Opacity using System;

using System.Threading; 

using Microsoft.SPOT;

using Microsoft.SPOT.Hardware;

using Microsoft.SPOT.Presentation.Media;

namespace OpacityWithRectanglesSample

{

class Program

{

static void Main(string[] args)

{

ScreenMetrics metrics = ScreenMetrics.GetMetrics();

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

//drawing a white background

bmp.DrawRectangle(Color.White, // outline color

0, // outline thickness

0, 0, // x and y coordinates

bmp.Width, bmp.Height, // width and height

0, 0, // x and y corner radius

Color.White, // gradient start color

0, 0, // gradient start coordinates

Color.White, // gradient end color

0, 0, // gradient end coordinates

Bitmap.OpacityOpaque); // reduced opacity

Color[] colors = new Color[] {

ColorUtility.ColorFromRGB(0xFF, 0, 0), // red

ColorUtility.ColorFromRGB(0, 0xFF, 0), // green

ColorUtility.ColorFromRGB(0, 0, 0xFF) // blue

};

for (int i = 0; i < colors.Length; ++i)

{

Color color = colors[i];

bmp.DrawRectangle(color, // outline color

0, // outline thickness

50 + i * 20, // x

50 + i * 20, // y

200, 100, // width and height

0, 0, // x and y corner radius

color, // gradient start color

0, 0, // gradient start coordinates

color, // gradient end color

0, 0, // gradient end coordinates

64); // reduced opacity

}

bmp.Flush();

Thread.Sleep(-1); //do not terminate app to see result

}

}

}

Drawing Rectangles with Rounded Corners

In the previous examples, every rectangle was drawn with the corner radius values set to 0, so the rectangles were not drawn with rounded corners. Figure 11-4 shows a rectangle with rounded corners using the same corner radius of 30 pixels for x and y. The rectangle shown in Figure 11-5 was produced using different corner radius values for x and y.

Figure 11-4. Rectangle with a corner radius of 30 pixels for x and y Figure 11-5. Rectangle with a 10-pixel x radius and 30-pixel y radius

■Caution The current release of the .NET Micro Framework does not allow you to use rounded corners and filling with rectangles at the same time. If you indicate a corner radius greater than zero, your rectangle will be drawn as a frame.

Drawing Rectangles with Color Gradients

In the preceding examples, all filled rectangles were drawn entirely filled with one color. By specifying different values for the start and end colors, you can achieve interesting color gradient effects. The code in Listing 11-5 produces a rectangle filled with a diagonal color gradient that starts in the top-left corner of the rectangle with white and ends in the bottom-right corner with black, as shown in Figure 11-6.

Figure 11-6. Diagonal color gradient

Listing 11-5. Drawing a Diagonal Color Gradient

bmp.DrawRectangle(Color.White, // outline color
1, // outline thickness
100, 100, // x and y of top left corner
200, 100, // width and height
0, 0, // x and y corner radius
Color.White, // gradient start color
100, 100, // gradient start coordinates
Color.Black, // gradient end color
100 + 200, 100 + 100, // gradient end coordinates
Bitmap.OpacityOpaque); // opacity

By specifying the same x coordinate for the gradient start and end colors, you achieve a vertical color gradient, as illustrated in Figure 11-7. Using the same y coordinate for start and end colors, you will produce a horizontal color gradient.

The gradient start and end coordinates are specified as screen coordinates, and they can lie outside of the rectangle. This causes the system to produce a color gradient starting at the start point with the start color and ending at the end point with the end color but showing only the part within your rectangle.

Figure 11-7. Vertical color gradient

Drawing Ellipses

The Bitmap class exposes the DrawEllipse method, which allows you to draw an ellipse. The method accepts a center point and x and y radii:

public void DrawEllipse(Color colorOutline,

int x, int y,

int xRadius,

int yRadius);

■Caution There is a second overload of the DrawEllipse method that accepts a thickness for the outline, color gradient settings, and an opacity value. With the present release of the .NET Micro Framework, none of these additional parameters work. You should not use this method. That means that, currently, you cannot draw filled circles or ellipses. If you need to render filled ellipses, a good but less flexible idea is to create a bitmap, add it to your application as a resource, and render it to the screen as we will discuss in the next section.

Drawing Images

In the following sections, you will learn how to draw one bitmap on another using different methods and techniques.

Drawing Full-Size Images

The Bitmap class provides a DrawImage method, which allows one bitmap to draw a part or all of another bitmap on itself.

The code in Listing 11-6 gets a bitmap from a resource, as explained in Chapter 10’s discussion about globalization and localization. Then, the entire ball bitmap is rendered in its original size using the DrawImage method (see Figure 11-8). This method allows you to draw either the entire source image or only a part of it by varying the source coordinates and size. This kind of drawing does not perform any scaling; scaling an image is discussed in the next section. Using the opacity parameter, the entire source image can be blended with the destination image. Later in this chapter, we’ll discuss how you can render an image with a transparent background.

Listing 11-6. Rendering the Full-Size Ball Image

ScreenMetrics metrics = ScreenMetrics.GetMetrics(); 
Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);
Bitmap soccerBall = Resources.GetBitmap(Resources.BitmapResources.SoccerBall); bmp.DrawImage(100, 50, // destination coordinates
soccerBall, // source image
0, 0, // source coordinates
soccerBall.Width, // source width
soccerBall.Height, // source height
Bitmap.OpacityOpaque); // opacity
bmp.Flush();

Figure 11-8. The full-size ball image

Drawing Scaled Images

The Bitmap class also provides a StretchImage method that allows one bitmap to draw all of another bitmap on itself by scaling the image to fit into a specified destination rectangle. You cannot draw a part of the source image; this drawing method will always render the entire source image but in a new size. The code in Listing 11-7 will render the entire ball image and perform a horizontal shrink and vertical stretch operation to fit the image into a destination rectangle that has half the width and double the height of the source image size (see Figure 11-9).

Listing 11-7. Image Scaling

ScreenMetrics metrics = ScreenMetrics.GetMetrics(); 

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

Bitmap soccerBall = Resources.GetBitmap(Resources.BitmapResources.SoccerBall); bmp.StretchImage(100, 50, // destination coordinates

soccerBall, // source image

soccerBall.Width / 2, // half width

soccerBall.Height * 2, // double height

Bitmap.OpacityOpaque); // opacity

bmp.Flush();

Figure 11-9. Scaling the ball image to half its width and double its height

Drawing Images with Transparent Backgrounds

With the opacity parameter, you select whether an entire bitmap will be rendered transparent, opaque, or something between. In contrast, the MakeTransparent method enables you to select a transparent color to draw, for example, the ball image with a transparent background. The MakeTransparent method accepts a color value. When rendering such a bitmap, each pixel of that color will not cover the underlaying pixels of the destination image that it’s drawing on.

For the ball image, we will obtain the color of the top-left corner pixel and choose it as the transparent color (see Listing 11-8).

Listing 11-8. Selecting a Transparent Color

ScreenMetrics metrics = ScreenMetrics.GetMetrics(); 

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

Bitmap soccerBall = Resources.GetBitmap(Resources.BitmapResources.SoccerBall);

//make background of the ball transparent

//using the color of top left corner pixel

soccerBall.MakeTransparent(soccerBall.GetPixel(0, 0));

bmp.DrawImage(100, 50, // destination coordinates

soccerBall, // source image

0, 0, // source coordinates

soccerBall.Width, // source width

soccerBall.Height, // source height

Bitmap.OpacityOpaque); // opacity

bmp.Flush();

The .NET Micro Framework does not support modifying images of the Bitmap type after you create them. If you embed an image resource in your application from a bitmap file, calling the MakeTransparent method for such an image will cause an error. With JPEG images, you will not get an exception, but the image will not be rendered transparently. With GIF images, the system behaves as expected. Therefore, if using a bitmap or JPEG resource, you need to create a copy of the image and then make it transparent (see Listing 11-9). The best option is to use GIF images and then make them transparent by selecting one transparent color.

Listing 11-9. Using a Transparent Background with Bitmap or JPEG Files

Bitmap soccerBall = Resources.GetBitmap(Resources.BitmapResources.SoccerBall); 

// create a copy

Bitmap soccerBallTransparent = new Bitmap(soccerBall.Width, soccerBall.Height); soccerBallTransparent.DrawImage(0, 0, // destination coordinates soccerBall, // source image

0, 0, // source coordinates soccerBall.Width, // source width

soccerBall.Height, // source height Bitmap.OpacityOpaque); // opacity

//make background of the ball transparent

//using the color of top left corner pixel

soccerBallTransparent.MakeTransparent(soccerBallTransparent.GetPixel(0, 0));

Listing 11-10 demonstrates how to program a moving ball that bounces back when it reaches the screen borders. The ball is rendered with a transparent background onto a color gradient background, as shown in Figure 11-10. In this example, the moving sprite is drawn flicker free using double buffering. That means each frame is prepared completely in a memory image buffer before flushing it to the display.

Before flushing, the code first loads the embedded sprite bitmap from the resources and initializes the background and the buffer bitmap that holds the current screen. In an endless loop, the application moves the sprite. The current screen position of the sprite (defined by the top-left corner of the sprite) is stored in the x and y variables. The variables xOfs and yOfs describe the moving direction and speed of the sprite. Every time the ball hits a border, both directions are inverted. The application changes the horizontal and vertical position with an equal absolute value, which causes the ball to move and bounce at a 45-degree angle.

Figure 11-10. A moving ball on a color gradient background

Listing 11-10. Programming Sprites

using System; 
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation.Media;
namespace BouncingBallSample
{
public class Program
{
public static void Main()
{
ScreenMetrics metrics = ScreenMetrics.GetMetrics();
//prepare background with a color gradient
Bitmap backgroundImg = new Bitmap(metrics.Width, metrics.Height);
backgroundImg.DrawRectangle( Color.White, // outline color
0, // outline thickness
0, 0, // x and y of top left corner
backgroundImg.Width, // width
backgroundImg.Height, // height
0, 0, // x and y corner radius
Color.White, // gradient start color
0, 0, // gradient start coordinates
Color.Black, // gradient end color
backgroundImg.Width, // gradient end x coordinate
backgroundImg.Height, // gradient end y coordinate
Bitmap.OpacityOpaque);// opacity
//prepare a working buffer to hold graphics before flushing to display
Bitmap bufferImg = new Bitmap(metrics.Width, metrics.Height);
//our ball
Bitmap soccerBallImg = Resources.GetBitmap(Resources.BitmapResources.SoccerBall);
//make background of the ball transparent
//using the color of top left corner pixel
soccerBallImg.MakeTransparent(soccerBallImg.GetPixel(0, 0));
int x = 100;
int y = 50;
int xOfs = 1;
int yOfs = 1;
while (true)
{
//copy background to buffer
bufferImg.DrawImage(0, 0, backgroundImg, 0, 0, backgroundImg.Width, backgroundImg.Height);
//paint moving sprite object
bufferImg.DrawImage(x, y, // destination coordinates
soccerBallImg, // source image
0, 0, // source coordinates
soccerBallImg.Width,
soccerBallImg.Height,
Bitmap.OpacityOpaque);
bufferImg.Flush(); //flush buffer to display
Thread.Sleep(10);
//invert direction if ball bounces at a wall
if (x <= 0 || x >= metrics.Width - soccerBallImg.Width)
xOfs = -xOfs;
if (y <= 0 || y >= metrics.Height - soccerBallImg.Height)
yOfs = -yOfs;
//calculate new coordinates
x += xOfs;
y += yOfs;
}
}
}
}

Drawing Text

This section presents various methods to render text using fonts.

Using Fonts

The .NET Micro Framework uses the .tinyfnt file format to work with fonts. This special format uses fixed size bitmaps for a simpler way to render fonts. Two sample .tinyfnt fonts are included in the Fonts subdirectory of your .NET Micro Framework installation. To create other fonts, in the size, style, and language tailored to your application, you can use the TFConvert tool to create a .tinyfnt font from TrueType or OpenType font files. That enables you to select the character sets and to even create fonts for displaying Cyrillic and Chinese characters. To get more information about creating .tinyfnt fonts with the TFConvert tool, please refer to the the TFConvert section (which includes useful web links) in Chapter 3 and the .NET Micro Framework SDK

documentation where this is described in detail.

The .NET Micro Framework SDK provides a set of sample OpenType fonts that you can use to create .tinyfnt fonts to use with your applications. The fonts are located in the \Tools\ Fonts\TrueType subdirectory of your .NET Micro Framework installation. The sample fonts are supplied under license from the Ascender Corporation. These fonts support a small character set that is a subset of the full font available from Ascender.

To use .tinyfnt fonts with your application, you need to embed such a font file into your assembly as a resource as described earlier. Once a .tinyfnt font file is created, you cannot change its size and style anymore; it is fixed. To use different sizes and styles, you need to create and use different fonts. The methods for rendering text accept a font value and a color value.

Drawing Text

The Bitmap class provides the DrawText method, which you can use to put text onto your display.

The method accepts the text to be drawn, as well as the font and color to use and the position in which to place the text. The coordinates specify the top-left corner of the text to be drawn.

Listing 11-11 demonstrates how you can create a bitmap that’s the size of the display, get an embedded font resource, and draw white text using the font (see Figure 11-11).

Listing 11-11. Drawing Text

ScreenMetrics metrics = ScreenMetrics.GetMetrics(); 

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

Font font = Resources.GetFont(Resources.FontResources.NinaB);

bmp.DrawText("Hello world.", // text

font, // font

Color.White, // color

20, 20); // x and y of top left corner

bmp.Flush();

Figure 11-11. Drawing text

Computing the Text Extent

You may want to calculate the size of a particular bit of text before drawing to make sure the text will fit correctly or to align the text. The Font class possesses a ComputeExtent method that enables you to do this.

The following code snippet will compute the size of a rectangle that is needed to show the text using the specified font. The method returns the two calculated width and height parameters. Because the method returns two parameters, it needs to use the out parameter construct to deliver the results instead of a single function return value.

Font myFont = Resources.GetFont(Resources.FontResources.NinaB);

int width, height;

myFont.ComputeExtent("Hello world.", out width, out height);

Drawing Text in a Rectangle

The DrawText method allows you to place a text at a particular position in your display. If you want to draw a text block and align and fit it within a box, DrawTextInRect will do this for you.

ScreenMetrics metrics = ScreenMetrics.GetMetrics(); 

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

Font font = Resources.GetFont(Resources.FontResources.NinaB);

bmp.DrawTextInRect("Using the DrawTextInRect method allows you " + "to draw a text block and align and fit it " + "within a box.",
20, 20, // x and y (rectangle top left)
200, 300, // width and height of rectangle
Bitmap.DT_AlignmentLeft | Bitmap.DT_WordWrap, // flags Color.White, // color
font); // font

bmp.Flush();

The DrawTextInRect method accepts different alignment and trimming flags that can be combined to achieve different effects. In this case, the flags are not, as you might expect, enumeration types. They are constant members of the Bitmap class. Figures 11-12 to 11-15 show text that was drawn using different flag combinations.

!Figure 11-12. Left alignment (DT_AlignmentLeft)

Figure 11-13. Left alignment with word wrapping (DT_AlignmentLeft | DT_WordWrap) Figure 11-14. Center alignment with word wrapping (DT_AlignmentCenter | DT_WordWrap) Figure 11-15. Right alignment with word wrapping (DT_AlignmentRight | DT_WordWrap) The possible alignment and trimming flags that you can combine follow:

• DT_AlignmentCenter
• DT_AlignmentLeft
• DT_AlignmentMask
• DT_AlignmentRight
• DT_IgnoreHeight
• DT_None
• DT_TrimmingCharacterEllipsis
• DT_TrimmingMask
• DT_TrimmingNone
• DT_TrimmingWordEllipsis
• DT_TruncateAtBottom
• DT_WordWrap

There is another overload of the DrawTextInRect method. That method comes along with reference parameters for the input string and the x and y drawing positions. After drawing text, the method updates the x and y coordinates to tell you where on the display the drawing of the text finished. This allows you to draw parts of the text with a different color or font. Also, if the method cannot display the complete text within the specified rectangle, it returns the remaining text. In this case, the method returns false to indicate that there is some text left that could not displayed. The updated x and y coordinates and the remaining text are returned to the caller via out parameters. This enables you to build up a display to show text over multiple pages.

Listing 11-12 will display the word “Hello” in white and “world” in blue (see Figure 11-16).

The updated text position is expressed relative to the rectangle but not in screen coordinates.

Figure 11-16. Text displayed in different colors

Listing 11-12. Displaying Text in Different Colors

ScreenMetrics metrics = ScreenMetrics.GetMetrics(); 

Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);

Font font = Resources.GetFont(Resources.FontResources.NinaB);

int x = 0;

int y = 0;

string text = "Hello ";

bmp.DrawTextInRect(ref text,

ref x, ref y, // relative x and y text position in rectangle 20, 20, // x and y (rectangle top left)

200, 300, // width and height of rectangle

Bitmap.DT_AlignmentLeft | Bitmap.DT_WordWrap,

Color.White, // color

font); // font

Color blueColor = ColorUtility.ColorFromRGB(0x0, 0x0, 0xFF);

text = "world";

bmp.DrawTextInRect(ref text,

ref x, ref y, // relative x and y text position in rectangle 20, 20, // x and y (rectangle top left)

200, 300, // width and height of rectangle

Bitmap.DT_AlignmentLeft | Bitmap.DT_WordWrap,

blueColor, // color

font); // font

bmp.Flush();

■Caution Leading spaces will be trimmed from the beginning of text; they will not be printed. Trailing spaces at the end of text will remain. Therefore, you need to append the space separating the two words to

“Hello” (don’t add it before “world”).

You might need to present so much text to the user that it won’t actually fit into one page.

In that case, you can display the information page by page and let the user scroll though the pages. The DrawTextInRect method helps you to build a paged text display (see Listing 11-13).

Figure 11-17 shows the first page of the paged text display and Figure 11-18 the second page.

Figure 11-17. First page of a paged text display

Figure 11-18. Second page of a paged text display

Listing 11-13. Paged Text Display

using System; 
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation.Media;
namespace PagedTextDisplaySample
{
public class Program
{
public static void Main()
{
ScreenMetrics metrics = ScreenMetrics.GetMetrics();
Bitmap bmp = new Bitmap(metrics.Width, metrics.Height);
Font font = Resources.GetFont(Resources.FontResources.NinaB);
string text = "There is another overload of the DrawTextInRect " +
"method. That method comes along with reference " +
"parameters for the input string and the x and y " +
"drawing positions. After drawing text, the " +
"method updates the x and y positions to tell you " +
"where on the display the drawing of the text " +
"finished. This allows you to draw parts of the text " +
"with a different color or font. Also, if the method " +
"cannot display the complete text within the specified " +
"rectangle, it returns the remaining text. " +
"In this case, the method returns false to indicate " +
"that there is some text left that could not be" +
"displayed. This enables you to build up a display " +
"to show text over multiple pages.";
bool completed;
do
{
int x = 0;
int y = 0;
//draw frame around text and clear old contents
bmp.DrawRectangle(Color.White, 1, 20, 20, 150, 150,
0, 0, Color.Black, 0, 0, Color.Black, 0, 0,
Bitmap.OpacityOpaque);
completed = bmp.DrawTextInRect(
ref text,
ref x, ref y, // x and y text position
20, 20, // x and y (rectangle top left)
150, 150, // width and height of rectangle
Bitmap.DT_AlignmentLeft | Bitmap.DT_WordWrap,
Color.White, // color
font); // font
bmp.Flush();
Thread.Sleep(3000); //display each page for three seconds
} while (!completed);
Thread.Sleep(-1);
}
}
}