- •Contents
- •What Is C#?
- •C# Versus Other Programming Languages
- •Preparing to Program
- •The Program Development Cycle
- •Your First C# Program
- •Types of C# Programs
- •Summary
- •Workshop
- •C# Applications
- •Basic Parts of a C# Application
- •Structure of a C# Application
- •Analysis of Listing 2.1
- •Object-Oriented Programming (OOP)
- •Displaying Basic Information
- •Summary
- •Workshop
- •Variables
- •Using Variables
- •Understanding Your Computer’s Memory
- •C# Data Types
- •Numeric Variable Types
- •Literals Versus Variables
- •Constants
- •Reference Types
- •Summary
- •Workshop
- •Types of Operators
- •Punctuators
- •The Basic Assignment Operator
- •Mathematical/Arithmetic Operators
- •Relational Operators
- •Logical Bitwise Operators
- •Type Operators
- •The sizeof Operator
- •The Conditional Operator
- •Understanding Operator Precedence
- •Converting Data Types
- •Understanding Operator Promotion
- •For Those Brave Enough
- •Summary
- •Workshop
- •Controlling Program Flow
- •Using Selection Statements
- •Using Iteration Statements
- •Using goto
- •Nesting Flow
- •Summary
- •Workshop
- •Introduction
- •Abstraction and Encapsulation
- •An Interactive Hello World! Program
- •Basic Elements of Hello.cs
- •A Few Fundamental Observations
- •Summary
- •Review Questions
- •Programming Exercises
- •Introduction
- •Essential Elements of SimpleCalculator.cs
- •A Closer Look at SimpleCalculator.cs
- •Simplifying Your Code with Methods
- •Summary
- •Review Questions
- •Programming Exercises
- •Introduction
- •Lexical Structure
- •Some Thoughts on Elevator Simulations
- •Concepts, Goals and Solutions in an Elevator Simulation Program: Collecting Valuable Statistics for Evaluating an Elevator System
- •A Deeper Analysis of SimpleElevatorSimulation.cs
- •Class Relationships and UML
- •Summary
- •Review Questions
- •Programming Exercises
- •The Hello Windows Forms Application
- •Creating and Using an Event Handler
- •Defining the Border Style of the Form
- •Adding a Menu
- •Adding a Menu Shortcut
- •Handling Events from Menus
- •Dialogs
- •Creating Dialogs
- •Using Controls
- •Data Binding Strategies
- •Data Binding Sources
- •Simple Binding
- •Simple Binding to a DataSet
- •Complex Binding of Controls to Data
- •Binding Controls to Databases Using ADO.NET
- •Creating a Database Viewer with Visual Studio and ADO.NET
- •Resources in .NET
- •Localization Nuts and Bolts
- •.NET Resource Management Classes
- •Creating Text Resources
- •Using Visual Studio.NET for Internationalization
- •Image Resources
- •Using Image Lists
- •Programmatic Access to Resources
- •Reading and Writing RESX XML Files
- •The Basic Principles of GDI+
- •The Graphics Object
- •Graphics Coordinates
- •Drawing Lines and Simple Shapes
- •Using Gradient Pens and Brushes
- •Textured Pens and Brushes
- •Tidying up Your Lines with Endcaps
- •Curves and Paths
- •The GraphicsPath Object
- •Clipping with Paths and Regions
- •Transformations
- •Alpha Blending
- •Alpha Blending of Images
- •Other Color Space Manipulations
- •Using the Properties and Property Attributes
- •Demonstration Application: FormPaint.exe
- •Why Use Web Services?
- •Implementing Your First Web Service
- •Testing the Web Service
- •Implementing the Web Service Client
- •Understanding How Web Services Work
- •Summary
- •Workshop
- •How Do Web References Work?
- •What Is UDDI?
- •Summary
- •Workshop
- •Passing Parameters and Web Services
- •Accessing Data with Web Services
- •Summary
- •Workshop
- •Managing State in Web Services
- •Dealing with Slow Services
- •Workshop
- •Creating New Threads
- •Synchronization
- •Summary
- •The String Class
- •The StringBuilder Class
- •String Formatting
- •Regular Expressions
- •Summary
- •Discovering Program Information
- •Dynamically Activating Code
- •Reflection.Emit
- •Summary
- •Simple Debugging
- •Conditional Debugging
- •Runtime Tracing
- •Making Assertions
- •Summary
Windows Forms
332
PART III
In this chapter, we’ll look at the Graphical Device Interface (GDI) used by Windows Forms to create graphics. Notice the name is GDI+ not GDI.NET. This is because GDI+ is not solely a
.NET Framework API. It can also be used by unmanaged code. For the purposes of this chapter however, we’ll use it exclusively from Windows Forms.
The Basic Principles of GDI+
Like its predecessor GDI, GDI+ is an immediate mode graphics system. This means that as you send commands to the graphic interface, effects are seen on the device surface or in the display memory immediately. The other kind of graphics system, retained mode graphics, usually maintain hierarchies of objects that know how to paint themselves.
GDI+ uses brushes and pens to paint on the graphics surface, just like GDI did. Unlike GDI, though, you can use any graphical object on any surface at any time. GDI required you to create a graphics object, select it for use in a Device Context (DC), use the object, deselect it from the DC, and then destroy the object. If you didn’t do it in that order, you could get system resource leaks that would eventually cause all the memory in your system to be allocated away and the system would need a reboot. GDI+ is altogether friendlier to use because you don’t have to worry about selecting and deselecting objects in the right order.
As you might expect, the evolution of GDI+ reflects many of the recent changes in graphics card capabilities. Additions to your graphics arsenal include Alpha Blending or image transparency, antialiasing, transformations like rotation and scaling, plus many new innovations in the areas of color correction and region manipulation.
The Graphics Object
The root of all GDI+ graphics capabilities for .NET is the Graphics object. This is analogous to the Win32 DC and can be obtained explicitly from a window handle or is passed to your application from, say, the OnPaint event. To obtain a Graphics object in Managed C++, for example, you would write the following code:
HDC dc;
PAINTSTRUCT ps;
Graphics *pGraphics;
Dc=BeginPaint(hWnd,&ps) pGraphics=new Graphics(dc); // use the Graphics...
EndPaint(hWnd,&ps);
GDI+: The .NET Graphics Interface
333
CHAPTER 3.5
For the purposes of this chapter, we’ll assume that GDI+ operations will take place in the context of a Windows Forms application and that the Graphics object is handed to you in an event delegate. For example, we see the Graphics object that’s supplied in the PaintEventArgs in the following:
public void OnPaint(object sender, PaintEventArgs e)
{
// use e.Graphics.SomeMethod(...) here
Graphics Coordinates
Objects are painted onto the screen using coordinates that are mapped to no less than three systems. The World Coordinate system, which is an abstract concept of space within the graphics system, the Page Coordinate system, which is another abstract concept of physical dimension on a hypothetical page, and the Device Coordinate system, which is tied to the physical properties of the screen or printer upon which the graphics are rendered. To display anything onscreen, the graphical data that you write to the GDI+ graphics object has to be transformed several times. The reason for this is to allow you to deal with graphics in a device-independent way. For example, if you place a shape or some text in World Coordinates, you might want to ensure that the resulting graphic is displayed as though it were on a standard A4 sheet of paper. The image must look as if it’s on A4 both on the monitor and on any printer of any resolution. The World Coordinate is, therefore, mapped to a Page Coordinate that represents an 8 1/2” by 11” physical measurement. When the system comes to display the A4 page, the monitor might have a resolution of 72 dots per inch (DPI)—printer A has a resolution of 300 DPI and printer B has a resolution of 1200 by 400 DPI. Page space, a physical measurement, then needs to be mapped to Device Space so that, in reality, the monitor maps 8.5 inches across the page to 612 pixels on the monitor—2550 pixels on printer A and 10200 pixels on printer B.
GDI+ allows you to set the way that objects are transformed from the world to the page using the PageUnit, PageUnit, PageScale, and PageScale properties. You have no control over the transformation to device space. That all happens in the device drivers themselves. You can, however, read the device’s DPI with the DpiX and DpiY properties.
We’ll examine coordinate transformations in detail later in this chapter.
Drawing Lines and Simple Shapes
Drawing lines or the outline of a shape requires the use of a pen. Filling an area, particularly the interior of a shape, is accomplished with a brush. The simple Windows Forms example in Listing 3.5.1 shows these operations. This is a self-contained, hand-edited example, so Visual Studio.Net is not needed.
3.5
T GDI+:RAPHICSGNTERFACEI . HE NET
Windows Forms
334
PART III
NOTE
A word about graphics object management under GDI+ for Windows Forms. The garbage collection heap, as you know by now, does memory management under
.NET for you. Technically, there is no need to worry about reclaiming your objects by deleting them because the garbage collector will do it for you eventually.
This is also true for graphical operations that create pens and brushes. However, an intense graphics routing could create many thousands of pens or brushes during each draw cycle and, in circumstances such as this, the garbage collector might not be quick enough to reclaim all the objects, causing a large number of dead items to be left on the heap for a considerable period of time.
To minimize the amount of work the garbage collector does, you should, wherever possible, explicitly invoke the Dispose() method on pens, brushes, and other such objects. This reclaims the memory in the object and tells the garbage collector not to bother with them.
In certain examples, you will see the declaration of the object inline, for example,
DrawRectangle(new Pen(Color.Red),.... This will not cause a memory leak of the pen, it simply defers reclamation of the object when it’s no longer in scope. In other examples, you will see explicit use of, for example, myPen.Dispose(). This is the recommended method for highest memory performance.
LISTING 3.5.1 DrawLines.cs: Drawing Lines and Filling Rectangles
1:using System;
2:using System.Drawing;
3:using System.Windows.Forms;
5:class drawlines : System.Windows.Forms.Form
6:{
7:Timer t;
8:bool BackgroundDirty;
10:public void TickEventHandler(object sender, EventArgs e)
11:{
12:Invalidate();
13:}
14:
15:public void OnPaint(object sender, PaintEventArgs e)
16:{
17:// the current graphics object for
18:// this window is in the PaintEventArgs
19:
GDI+: The .NET Graphics Interface
CHAPTER 3.5
LISTING 3.5.1 Continued
20: Random r=new Random(); 21:
22:Color c=Color.FromArgb(r.Next(255),r.Next(255),r.Next(255));
23:Pen p=new Pen(c,(float)r.NextDouble()*10);
24:e.Graphics.DrawLine(p,r.Next(this.ClientSize.Width),
25: |
r.Next(this.ClientSize.Height), |
26: |
r.Next(this.ClientSize.Width), |
27: |
r.Next(this.ClientSize.Height)); |
28:p.Dispose();
29:}
30:
31:protected override void OnPaintBackground(PaintEventArgs e)
32:{
33:// When we resize or on the first time run
34:// we’ll paint the background, otherwise
35:// it will be left so the lines build up
36:if(BackgroundDirty)
37:{
38:BackgroundDirty = false;
39:// We could call base.OnPaintBackground(e);
40:// but that doesn’t show off rectangle filling
41:e.Graphics.FillRectangle(new SolidBrush(this.BackColor),
42: |
this.ClientRectangle); |
43: |
} |
44: |
|
45: |
} |
46: |
|
47:public void OnSized(object sender, EventArgs e)
48:{
49:BackgroundDirty=true;
50:Invalidate();
51:}
52:
53:public drawlines()
54:{
55:t=new Timer();
56:t.Interval=300;
57:t.Tick+=new System.EventHandler(TickEventHandler);
58:t.Enabled=true;
59:this.Paint+=new PaintEventHandler(OnPaint);
60:this.SizeChanged+=new EventHandler(OnSized);
61:this.Text=”Lines and lines and lines”;
62:BackgroundDirty = true;
63:}
335
3.5
T GDI+:RAPHICSGNTERFACEI . HE NET