Page Units


So far, we've been concentrating on drawing to the screen. By default, if you're drawing in the Paint event handler, you're drawing in units of pixels. Even if you create a graphics object from a form using Form.CreateGraphics, you'll be drawing in units of pixels. This is handy because the units of the user interface elements, such as the client rectangle and the position and sizes of the controls, are all in pixels.

Pixels translate into real-world coordinates based on system settings for Normal or Small versus Large or Custom fonts, the resolution at which the display adapter is running, and the size of the monitor. Taking all that into account, only some of which is available programmatically, it would be remarkably difficult to display physically correct sizes on a monitor ”for example, the ruler you see at the top of a word processing program. Luckily, because you can usually adjust all this using various systemwide and application-specific settings, people generally size things so that they are comfortable, and the real-world sizes are not so important. That is, they're not important until you need to output to a specific physical size.

For example, it's not important that the ruler at the top of the document I'm typing this sentence into is currently showing an inch as 1 inches. [1] What is important is the proportion of the dimensions of each line to the units shown as "inches" as compared to the width of each line as I type. The principle of WYSIWYG (What You See Is What You Get) dictates that I should be able to print something very similar to what I'm seeing on the screen. When my word processing program shows a line wrapping at a certain word when I get close to the 6.5-inch area inside my margins (standard 8.5-inch wide paper with a 1-inch margin on each side), I want that same wrap to happen at the same word when I print the document. To make that happen, we need to be able to write a program that can wrap text at units other than pixels, as shown in Figure 6.1.

[1] I measured it with a ruler from the physical world.

Figure 6.1. Manually Drawing in Inches

Figure 6.1 shows a ruler marked off in half-inch increments and text wrapped to a right margin of 6.5 inches. We can accomplish this by using the following function to manually convert coordinates and sizes to inches:

 float InchesToPixels(float inches) {   using( Graphics g = this.CreateGraphics() ) {     return inches * g.DpiX;   } } 

This function is used to calculate the width of the ruler, the half-inch tick marks, and the width of the text box. For example, the code that draws the outline of the ruler looks like this:

 using( Font rulerFont = new Font("MS Sans Serif", 8.25f) ) {  int pixelsPerInch = 72;   // Inches   float rulerFontHeight = rulerFont.SizeInPoints/pixelsPerInch;   // Specify units in inches   RectangleF rulerRect =   new RectangleF(   0, 0,   6.5f, rulerFontHeight * 1.5f);   // Draw in pixels  g.DrawRectangle(     Pens.Black,  InchesToPixels(rulerRect.X), InchesToPixels(rulerRect.Y),   InchesToPixels(rulerRect.Width), InchesToPixels(rulerRect.Height));  ... } 

The conversion from inches to pixels is necessary because the units of the Graphics object passed to the Paint event are pixels, which represent the device units for the display adapter. All units eventually need to be translated to device units for rendering, but this doesn't mean that you need to specify drawing in device units. Instead, the Graphics object is drawing using page units , which default to pixels in the Paint event but don't need to stay that way. The PageUnit and PageScale properties of the Graphics object allow you to specify different units in which to draw:

  // Set page units and scale   g.PageUnit = GraphicsUnit.Inch;   g.PageScale = 1; // 1 unit is 1 inch  using( Font rulerFont = new Font("MS Sans Serif", 8.25f) ) using( Pen blackPen = new Pen(Color.Black, 0) ) {  // Inches   float rulerFontHeight = rulerFont.GetHeight(g);   // Specify units in inches   RectangleF rulerRect =   new RectangleF(   0, 0,   6.5f, rulerFontHeight * 1.5f);   // Draw in inches   g.DrawRectangle(   blackPen,   rulerRect.X, rulerRect.Y,   rulerRect.Width, rulerRect.Height);  ... } 

Before the code does any drawing, the first thing it does is to set the page unit for the graphics object to GraphicsUnit.Inch [2] and the page scale to 1, which will turn every one unit, whether it's specified for a position or a size, into 1 inch. Notice that we're using floating point numbers to enable fractional inches; the floating point numbers will be converted to device units by the Graphics object. The PageUnit property can be any value from the GraphicsUnit enumeration, so units can be in points or millimeters as well as pixels or inches. The PageScale can be a floating point number, so if we had wanted to specify a scale of 0.1 when specifying a PageUnit of Inch, then 1 unit would equal 0.1 inch, and 10 units would equal 1 inch.

[2] Recall the GraphicsUnit enumeration from Chapter 4: Drawing Basics.

Note the use of a new black pen, in spite of the presence of the Pens.Black pen that was used in the earlier sample. All the default pens default to 1 unit in width. When the unit was pixels, that was fine, but when the unit is inches, a 1-unit pen became 1 inch wide. Pens are specified in units that are interpreted when the pen is used. To avoid having a very wide pen, the code specifies 0 for the width of the pen, and that causes the pen to be 1 device unit wide no matter what the page unit is currently set to.

Also note that the Font object is not affected by the page units. Instead, recall from Chapter 5: Drawing Text that Fonts are specified using a GraphicsUnit argument passed to the constructor, and they default to GraphicsUnit.Point. Finally, notice that the code uses the GetHeight method of the Font class, passing the Graphics object. Unlike the Height property, the GetHeight method is scaled appropriately to the current units of the Graphics object.

Converting Pixels to Page Units

If a method doesn't take a Graphics object as an argument, then it won't be affected by the page units. For example, the ClientRectangle of the form or control being drawn is always specified in pixels, making some consideration necessary when units other than pixels are being used. To convert back and forth between device and page units, the Graphics object provides the TransformPoints method:

 using( Graphics g = this.CreateGraphics() ) {  // Set page unit to inches   g.PageUnit = GraphicsUnit.Inch;   g.PageScale = 1;  PointF[] bottomRight =     new PointF[] {       new PointF(this.ClientSize.Width, this.ClientSize.Height)     };  // Convert client size to inches from pixels   g.TransformPoints(   CoordinateSpace.Page, CoordinateSpace.Device, bottomRight);  ... } 

The TransformPoints method can convert between any types of coordinates from the CoordinateSpace enumeration (from the System. Drawing.Drawing2D namespace). This code converts to page units (set to inches in this example) from device units (also known as pixels). The CoordinateSpace enumeration has the following values:

 enum CoordinateSpace {   Device,   Page,   World, } 

The value we haven't yet discussed is CoordinateSpace.World, which is a whole other world of coordinates (if you'll excuse the pun).



Windows Forms Programming in C#
Windows Forms Programming in C#
ISBN: 0321116208
EAN: 2147483647
Year: 2003
Pages: 136
Authors: Chris Sells

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net