Coordinate Systems
As with most graphics systems, the position of items on a Graphics surface is controlled by X and Y coordinates. A single coordinate is a pair of numbers that represent the distance across the surface from left to right and the distance down the surface from top to bottom. For example, a line may be drawn from place to place using the code shown in listing 1.
g.DrawLine(Pens.Black,0,0,100,200);
g.DrawLine(Pens.Black,0,0,100,200)
The coordinates are given in the last four parameters of the line drawing command as 0,0,100,200 and represent a line drawn from position 0,0 to position 100,200 as shown in figure 1.
Figure 1. In the default mode, the coordinates used in GDI+ refer to pixel positions but GDI+ is a Resolution Independent drawing system. This means that you can represent abstract drawing units such as pixels or real-world drawing units such as inches and millimeters in GDI+. Goodbye to integersMany graphics systems and particularly old Windows GDI used integer values for the coordinates. This meant that you could not represent an inch and a half by the number 1.5. In GDI+ this is a thing of the past because the coordinates used to place colour on the drawing surface are floating point numbers. This means that if you wish to create a CAD drawing program and show designs in accurate inch or millimeter sizes then you can with GDI+. Coordinate spacesThere are three distinct coordinate spaces in GDI+. These are;
The correct term for this type of system, where coordinates are transformed from one system to another over several steps, is a Graphics Pipeline. The GDI+ graphics pipeline takes the values you use and transforms them from an abstract floating point number to a real-world value and then to the hardware world of the monitor or printer. Real-world values.The actual real-world standards available to you for the Page Space are;
The following demonstration application shows how the real-world values can be used in your code. The application draws several rectangles that are created to inch, millimeter and pixel sizes. The application will print too so that you can check the sizes against a printed copy and on screen. If you have a plug and play monitor you'll see that the printed sizes and the screen sizes of the real-world values are identical but the pixel values aren't. Figure 1 shows the application at work.
Figure 1. Real-world drawing systems. The bit that does the drawing of the rectangles is shown in the following listing. private void DrawRectangles(Graphics g) { g.PageUnit=GraphicsUnit.Pixel; Pen p=new Pen(Color.Black, 3); //this pen will be 3 pixels wide g.DrawRectangle(p,10,10,200,100); //draw a rectangle in Pixel mode (the default) p.Dispose();
g.PageUnit=GraphicsUnit.Inch; p=new Pen(Color.Blue,0.05f); //this pen will be 1/20th of an inch wide g.DrawRectangle(p,0.1f,1.5f,4f,1f); // draw a rectangle 4" by 1" p.Dispose();
g.PageUnit=GraphicsUnit.Millimeter; p=new Pen(Color.Green,1f); //this pen will be 1 millimeter wide g.DrawRectangle(p,4f,80f,80f,60f); // draw a rectangle 80 by 60 mm p.Dispose(); } Private Sub DrawRectangles(ByVal g As Graphics) g.PageUnit = GraphicsUnit.Pixel Dim p As New Pen(Color.Black, 3) 'this pen will be 3 pixels wide g.DrawRectangle(p, 10, 10, 200, 100) 'draw a rectangle in Pixel mode (the default) p.Dispose()
g.PageUnit = GraphicsUnit.Inch p = New Pen(Color.Blue, 0.05F) 'this pen will be 1/20th of an inch wide g.DrawRectangle(p, 0.1F, 1.5F, 4.0F, 1.0F) ' draw a rectangle 4" by 1" p.Dispose()
g.PageUnit = GraphicsUnit.Millimeter p = New Pen(Color.Green, 1.0F) 'this pen will be 1 millimeter wide g.DrawRectangle(p, 4.0F, 80.0F, 80.0F, 60.0F) ' draw a rectangle 80 by 60 mm p.Dispose() End Sub 'DrawRectangles The same code, in DrawRectangles, is used for both printing and screen drawing. Note how, especially for the inch based drawing, floating point values with fractional numbers are used. The full listing of the code, shown in listing 2, includes the button click handler to print a single page with the rectangles drawn on it. Unless by some strange quirk of fate your printer resolution is identical to that of your screen you'll see two very different versions of the same code. using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Drawing.Printing;
namespace RealWorld { /// <summary> /// Summary description for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Button button1; /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null;
public Form1() { // // Required for Windows Form Designer support // InitializeComponent();
// // TODO: Add any constructor code after InitializeComponent call // }
/// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); }
#region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); this.button1 = new System.Windows.Forms.Button(); this.SuspendLayout(); // // panel1 // this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panel1.BackColor = System.Drawing.Color.White; this.panel1.Location = new System.Drawing.Point(8, 8); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(312, 320); this.panel1.TabIndex = 0; this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1_Paint); // // button1 // this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.button1.Location = new System.Drawing.Point(336, 40); this.button1.Name = "button1"; this.button1.TabIndex = 1; this.button1.Text = "Print"; this.button1.Click += new System.EventHandler(this.button1_Click); // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(416, 334); this.Controls.Add(this.button1); this.Controls.Add(this.panel1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false);
} #endregion
/// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); }
private void DrawRectangles(Graphics g) { g.PageUnit=GraphicsUnit.Pixel; Pen p=new Pen(Color.Black, 3); //this pen will be 3 pixels wide g.DrawRectangle(p,10,10,200,100); //draw a rectangle in Pixel mode (the default) p.Dispose();
g.PageUnit=GraphicsUnit.Inch; p=new Pen(Color.Blue,0.05f); //this pen will be 1/20th of an inch wide g.DrawRectangle(p,0.1f,1.5f,4f,1f); // draw a rectangle 4" by 1" p.Dispose();
g.PageUnit=GraphicsUnit.Millimeter; p=new Pen(Color.Green,1f); //this pen will be 1 millimeter wide g.DrawRectangle(p,4f,80f,80f,60f); // draw a rectangle 80 by 60 mm p.Dispose(); }
private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { DrawRectangles(e.Graphics); }
private void button1_Click(object sender, System.EventArgs e) { PrintDocument pd=new PrintDocument(); pd.PrintPage+=new PrintPageEventHandler(pd_PrintPage); pd.Print(); }
private void pd_PrintPage(object sender, PrintPageEventArgs e) { DrawRectangles(e.Graphics); e.HasMorePages=false; } } }
Imports System Imports System.Drawing Imports System.Collections Imports System.ComponentModel Imports System.Windows.Forms Imports System.Data Imports System.Drawing.Printing
Namespace RealWorld '/ <summary> '/ Summary description for Form1. '/ </summary>
Public Class Form1 Inherits System.Windows.Forms.Form Private WithEvents panel1 As System.Windows.Forms.Panel Private WithEvents button1 As System.Windows.Forms.Button '/ <summary> '/ Required designer variable. '/ </summary> Private components As System.ComponentModel.Container = Nothing
Public Sub New() ' ' Required for Windows Form Designer support ' InitializeComponent() End Sub 'New
' ' TODO: Add any constructor code after InitializeComponent call '
'/ <summary> '/ Clean up any resources being used. '/ </summary> Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Dispose
#Region "Windows Form Designer generated code"
'/ <summary> '/ Required method for Designer support - do not modify '/ the contents of this method with the code editor. '/ </summary> Private Sub InitializeComponent() Me.panel1 = New System.Windows.Forms.Panel Me.button1 = New System.Windows.Forms.Button Me.SuspendLayout() ' ' panel1 ' Me.panel1.Anchor = CType(System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles) Me.panel1.BackColor = System.Drawing.Color.White Me.panel1.Location = New System.Drawing.Point(8, 8) Me.panel1.Name = "panel1" Me.panel1.Size = New System.Drawing.Size(312, 320) Me.panel1.TabIndex = 0 ' ' button1 ' Me.button1.Anchor = CType(System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right, System.Windows.Forms.AnchorStyles) Me.button1.Location = New System.Drawing.Point(336, 40) Me.button1.Name = "button1" Me.button1.TabIndex = 1 Me.button1.Text = "Print" ' ' Form1 ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(416, 334) Me.Controls.Add(button1) Me.Controls.Add(panel1) Me.Name = "Form1" Me.Text = "Form1" Me.ResumeLayout(False) End Sub 'InitializeComponent #End Region
'/ <summary> '/ The main entry point for the application. '/ </summary> <STAThread()> _ Shared Sub Main() Application.Run(New Form1) End Sub 'Main
Private Sub DrawRectangles(ByVal g As Graphics) g.PageUnit = GraphicsUnit.Pixel Dim p As New Pen(Color.Black, 3) 'this pen will be 3 pixels wide g.DrawRectangle(p, 10, 10, 200, 100) 'draw a rectangle in Pixel mode (the default) p.Dispose()
g.PageUnit = GraphicsUnit.Inch p = New Pen(Color.Blue, 0.05F) 'this pen will be 1/20th of an inch wide g.DrawRectangle(p, 0.1F, 1.5F, 4.0F, 1.0F) ' draw a rectangle 4" by 1" p.Dispose()
g.PageUnit = GraphicsUnit.Millimeter p = New Pen(Color.Green, 1.0F) 'this pen will be 1 millimeter wide g.DrawRectangle(p, 4.0F, 80.0F, 80.0F, 60.0F) ' draw a rectangle 80 by 60 mm p.Dispose() End Sub 'DrawRectangles
Private Sub panel1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles panel1.Paint DrawRectangles(e.Graphics) End Sub 'panel1_Paint
Private Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button1.Click Dim pd As New PrintDocument AddHandler pd.PrintPage, AddressOf pd_PrintPage pd.Print() End Sub 'button1_Click
Private Sub pd_PrintPage(ByVal sender As Object, ByVal e As PrintPageEventArgs) DrawRectangles(e.Graphics) e.HasMorePages = False End Sub 'pd_PrintPage End Class 'Form1 End Namespace 'RealWorld |