A Drawing Program
Vector graphics deals with drawing geometric
shapes
such as lines, curves, and polygons. In this section we
illustrate
vector graphics in GDI+ with an interactive drawing program. Later in the chapter we will introduce raster graphics and bitmaps.
Our sample program,
DrawDemo
, allows you to draw a picture consisting of rectangles and ellipses of various sizes, colors, and patterns. The program illustrates both standard painting (implemented by an override of
OnPaint
) and "rubber-
band
" drawing, in which a shape is temporarily outlined as the mouse is moved. We also illustrate use of a clipping rectangle, which can make for more efficient drawing.
Rubber-Band Drawing
Version 1 of
DrawDemo
illustrates drawing a single rectangle. When the program is started, a small square is shown in the top-left area of the client window. Pressing a mouse button erases the current rectangle (by redrawing the rectangle with the background
color
of the container) and begins a new drawing. As the mouse is moved, with the button still pressed, a rectangle is outlined. When the mouse button is released, the current rectangle is "locked" and will be always displayed via
OnPaint
. Here is the code:
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Private m_start As Point
Private m_end As Point
Private m_track As Boolean
Private Function MakeRectangle(ByVal p As Point, _
ByVal q As Point) As Rectangle
Dim top, left, bottom, right As Integer
top = IIf(p.Y < q.Y, p.Y, q.Y)
left = IIf(p.X < q.X, p.X, q.X)
bottom = IIf(p.Y > q.Y, p.Y, q.Y)
right = IIf(p.X > q.X, p.X, q.X)
Return New Rectangle(left, top, right - left, _
bottom - top)
End Function
Protected Overrides Sub OnPaint(_
ByVal e As System.Windows.Forms.PaintEventArgs)
Dim rect As Rectangle
rect = MakeRectangle(m_start, m_end)
Dim g As Graphics = e.Graphics
g.DrawRectangle(Pens.Black, rect)
MyBase.OnPaint(e)
End Sub
Protected Overrides Sub OnMouseDown(_
ByVal e As System.Windows.Forms.MouseEventArgs)
m_start = New Point(e.X, e.Y)
m_end = m_start
m_track = True
Invalidate()
End Sub
Protected Overrides Sub OnMouseUp(_
ByVal e As System.Windows.Forms.MouseEventArgs)
m_track = False
Invalidate()
End Sub
Protected Overrides Sub OnMouseMove(_
ByVal e As System.Windows.Forms.MouseEventArgs)
If m_track Then
Dim p As New Point(e.X, e.Y)
If Not p.Equals(m_start) Then
Dim g As Graphics = Me.CreateGraphics()
Dim rect As Rectangle
' erase previous rectangle
rect = MakeRectangle(m_start, m_end)
g.DrawRectangle(Pens.White, rect)
' draw new rectangle
m_end = p
rect = MakeRectangle(m_start, m_end)
g.DrawRectangle(Pens.Black, rect)
End If
End If
End Sub
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
m_start = New Point(5, 5)
m_end = New Point(50, 50)
End Sub
End Class
Multiple Shapes
|
Version 2 of
DrawDemo
is a more full-blown program, allowing the user to choose among different shapes and to specify a color and a brush. With a toolbar, the
user
can choose between a rectangle and an ellipse (these choices are also available from the Draw menu). The menu Draw Brush
brings
up a modeless dialog box,
[1]
which allows the user to specify the style of the brush. By clicking Color, the user can pick a color. The
chosen
color and a text string for the shape are shown in an edit control.
[1]
See Chapter 12 for a discussion of how you can create your own modeless dialog boxes using Windows Forms.
|
Note that the settings chosen from the Brush Properties dialog apply to the
next
shape that is drawn. Each shape that is drawn is stored in a list of shapes. Figure 11-7 shows a simple drawing created using this program, consisting of a rectangle and two ellipses, with different colors and hatch styles.
Figure 11-7. With drawing program, you can draw various shapes.
Shape Hierarchy
This kind of program can be implemented by using a hierarchy of shapes. For simplicity, our hierarchy only includes rectangles and ellipses. The file
Shape.vb
provides the code for this hierarchy. The key feature is that the base class
CShape
is an abstract class with a
MustOverride
method
DrawShape
. The
DrawShape
method of the derived class draws the shape corresponding to the class. Hence
DrawShape
can be used polymorphically.
' Shape.vb
Imports System.Drawing
Public MustInherit Class CShape
Protected m_p1 As Point
Protected m_p2 As Point
Protected m_pen As Pen
Protected m_brush As Brush
Public Sub New(ByVal p1 As Point, ByVal p2 As Point, _
ByVal pn As Pen, ByVal br As Brush)
Dim rect As Rectangle
rect = MakeRectangle(p1, p2)
m_p1 = rect.Location
m_p2 = New Point(rect.Right, rect.Bottom)
m_pen = pn
m_brush = br
End Sub
Public Sub New(ByVal p1 As Point, ByVal p2 As Point)
m_pen = Pens.Black
m_brush = Brushes.Transparent
Dim rect As Rectangle
rect = MakeRectangle(p1, p2)
m_p1 = rect.Location
m_p2 = New Point(rect.Right, rect.Bottom)
End Sub
Public MustOverride Sub DrawShape(ByVal g As Graphics)
Private Function MakeRectangle(ByVal p As Point, _
ByVal q As Point) As Rectangle
Dim top, left, bottom, right As Integer
top = IIf(p.Y < q.Y, p.Y, q.Y)
left = IIf(p.X < q.X, p.X, q.X)
bottom = IIf(p.Y > q.Y, p.Y, q.Y)
right = IIf(p.X > q.X, p.X, q.X)
Return New Rectangle(left, top, right - left, _
bottom - top)
End Function
End Class
Public Class CRectangle
Inherits CShape
Public Sub New(ByVal p1 As Point, ByVal p2 As Point, _
ByVal pn As Pen, ByVal br As Brush)
MyBase.New(p1, p2, pn, br)
End Sub
Public Sub New(ByVal p1 As Point, ByVal p2 As Point)
MyBase.New(p1, p2)
End Sub
Public Overrides Sub DrawShape(ByVal g As Graphics)
Dim rect As New Rectangle(m_p1.X, m_p1.Y, _
m_p2.X
-
m_p1.X, m_p2.Y
-
m_p1.Y)
g.FillRectangle(m_brush, rect)
g.DrawRectangle(m_pen, rect)
End Sub
End Class
An Arraylist of Shapes
The main form can store the shape objects in an array list. The handler for the MouseUp event adds a shape to the list.
...
Private m_list As New ArrayList()
...
Private Function MakeShape(ByVal p As Point, _
ByVal q As Point, ByVal stype As ShapeType) As CShape
Select Case stype
Case ShapeType.Ellipse
Return New CEllipse(p, q, m_pen, m_brush)
Case ShapeType.Rectangle
Return New CRectangle(p, q, m_pen, m_brush)
End Select
End Function
...
Protected Overrides Sub OnMouseUp(_
ByVal e As MouseEventArgs)
m_track = False
m_shape = MakeShape(m_start, m_end, m_type)
m_list.Add(m_shape)
Invalidate()
End Sub
Displaying the Shapes
The shapes are displayed in the override of
OnPaint
by looping through the array list. Notice how polymorphism makes this code very
concise
.
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
Dim objShape As CShape
For Each objShape In m_list
objShape.DrawShape(g)
Next
MyBase.OnPaint(e)
End Sub
|