Whereas the Brush classes are used to fill shapes, the Pen class is used to frame shapes . The interesting members are shown here: NotInheritable Class Pen Inherits MarshalByRefObject Implements ICloneable Implements IDisposable 'Constructors Public Sub New(abrush as Brush) Public Sub New(abrush As Brush, width As Single) Public Sub New(acolor As Color) Public Sub New(acolor As Color, width As Single) ' Properties Property Alignment() As PenAlignment Property Brush() As Brush Property Color() As Color Property CompoundArray() As Single() Property CustomEndCap() As CustomLineCap Property CustomStartCap() As CustomLineCap Property DashCap() As DashCap Property DashOffset() As Single Property DashPattern() As Single() Property DashStyle() As DashStyle Property EndCap() As LineCap Property LineJoin() As LineJoin Property MiterLimit() As Single Property PenType() As PenType Property StartCap() As LineCap Property Width() As Single ' Transformation members elided ' Methods Public Sub SetLineCap(...) End Class Pens have several interesting properties, including a width, a color or a brush, start and end cap styles, and a dash pattern for the line itself. One note of interest is that the width of a pen is specified in the units of the underlying Graphics being drawn on (more information about Graphics units is available in Chapter 6: Advanced Drawing). However, no matter what the underlying units, a pen width of 0 always translates into a width of 1 physical unit on the underlying Graphic surface. This lets you specify the smallest visible pen width without worrying about the units of a particular surface. You'll notice that the Pen class is sealed (NotInheritable), which means that it can't be used as a base class for further penlike functionality. Instead, each pen has a type that governs its behavior, as determined by the PenType enumeration: Enum PenType SolidColor ' Created from a color or a SolidBrush TextureFill ' Created from a TextureBrush HatchFill ' Created from a HatchBrush LinearGradient ' Created from a LinearGradientBrush PathGradient ' Created from a PathGradientBrush End Enum If you're interested in common, solid-color pens, the 141 named pens are provided as static Pen properties on the Pens class, and 15 system pens are provided as static Pen properties on the SystemPens class, providing the same usage as the corresponding Brushes and SystemBrushes classes. As with SystemBrushes, the FromSystemColor method of the SystemPens class returns a pen in one of the system colors that's managed by .NET. Line CapsIn addition to their brushlike behavior, pens have behavior at ends and joints and along their length that brushes don't have. For example, each end can have a different style, as determined by the LineCap enumeration shown in Figure 4.9. Figure 4.9. Examples from the LineCap Enumeration
All these lines were generated with a black pen of width 12 passed to the Graphics.DrawLine method. The white line of width 1 in the middle is drawn using a separate call to Graphics.DrawLine to show the two end points that define the line. Each black pen is defined with the EndCap property set to a value from the LineCap enumeration: Dim mypen As Pen = New Pen(Color.Black, 12) pen.EndCap = LineCap.Flat ' default g.DrawLine(mypen, x, CInt(y + height * 2/3), _ CInt(x + width * 2/3), CInt(y + height * 2/3)) g.DrawLine(whitePen, x, CInt(y + height * 2/3), _ CInt(x + width * 2/3), CInt(y + height * 2/3)) ... mypen.Dispose() The default line cap style is flat, which is what all the StartCap properties are set to. You'll notice some familiar line cap styles, including flat, round, square, and triangle, which have no anchor, as well as arrow, diamond, round, and square, which have anchors. An anchor indicates that part of the line cap extends beyond the width of the pen. The difference between square and flat, on the other hand, dictates whether the line cap extends beyond the end of the line (as square does, but flat does not). You can manage these kinds of drawing behaviors independently by using the LineCap.Custom enumeration value and setting the CustomStartCap or CustomEndCap field to a class that derives from the CustomLineCap class (from the System.Drawing.Drawing2D namespace). The custom line cap in Figure 4.9 shows a pen created using an instance of the AdjustableArrowCap class, the only custom end cap class that .NET provides: Dim mypen As Pen = New Pen(Color.Black, 12) pen.EndCap = LineCap.Custom ' width and height of 3 and unfilled arrow head pen.CustomEndCap = New AdjustableArrowCap(3.0F, 3.0F, False) ... mypen.Dispose() DashesIn addition to the ends having special styles, a line can have a dash style, as defined by the DashStyle enumeration, shown in Figure 4.10. Figure 4.10. Examples Using the DashStyle Enumeration
Each of the lines was created by setting the DashStyle property of the pen. The DashStyle.Custom value is used to set custom dash and space lengths, where each length is a multiplier of the width. For example, the following code draws the increasing length dashes shown in Figure 4.10 with a constant space length: Dim mypen As Pen = New Pen(Color.Black, 12) pen.DashStyle = DashStyle.Custom ' Set increasing dashes and constant spaces pen.DashPattern = New Single() { 1.0F, 1.0F, 2.0F, _ 1.0F, 3.0F, 1.0F, 4.0F, 1.0F } g.DrawLine(mypen, x + 10, CInt(y + height*2/3), _ CInt(x + width 20), CInt(y + height*2/3)) ... mypen.Dispose() If you'd like to exercise more control over your custom dash settings, you can set the DashCap property on the pen to any of the values in the DashCap enumeration, which is a subset of the values in the LineCap enumeration with only Flat (the default), Round, and Triangle. To exercise more control over the line itself, in addition to dash settings, you can define compound pens using the CompoundArray property. This allows you to provide lines and spaces in parallel to the lines being drawn instead of perpendicularly, as dash settings do. For example, Figure 4.11 was drawn with a pen set up this way: Dim mypen As Pen = New Pen(Color.Black, 20) ' Set percentages of width where line starts, then space starts, ' then line starts again, etc. in alternating pattern mypen.CompoundArray = New Single() { 0.0F, 0.25F, 0.45F, 0.55F, 0.75F, _ 1.0F } g.DrawRectangle(mypen, New Rectangle()) mypen.Dispose() Figure 4.11. A Single Rectangle Drawn with a Pen Using a Compound Array
AlignmentsMost of the examples, including Figure 4.11, have shown pens of width greater than 1. When you draw a line of width greater than 1, the question is, where do the extra pixels go ”above the line being drawn, below it, or somewhere else? The default pen alignment is centered , which means that half the width goes inside the shape being drawn and the other half goes outside. The alignment can also be inset , which means that the entire width of the pen is inside the shape being drawn, as shown in Figure 4.12. Figure 4.12. Pen Alignment Options (See Plate 11)
In Figure 4.12, both ellipses are drawn using a rectangle of the same dimensions (as shown by the red line), but the different alignments determine where the width of the line is drawn. There are actually several values in the PenAlignment enumeration, but only Center and Inset are currently supported, and Inset is used only for closed shapes (an open figure has no "inside"). JoinsOne final consideration you'll have when drawing figures that have angles is what to do with the line at the angle. In Figure 4.13, the four values in the PenJoin enumeration have been set in the Pen class's LineJoin property before the rectangles were drawn (again, a white line of width 1 is used to show the shape being drawn). Figure 4.13. Sample PenJoin Values
Notice in Figure 4.13 that each corner provides a different join. The one exception is MiterClipped, which changes between Bevel and Miter dynamically based on the angle of the corner and the limit set by the MiterLimit property. Creating Pens from BrushesSo far in this section on pens, all the examples have used solid-color pens. However, you can also create a pen from a brush. For example, Figure 4.14 shows an image you first encountered in Chapter 1: Hello, Windows Forms. Figure 4.14. Creating a Pen from a LinearGradientBrush
The pen used to draw the lines in Figure 4.14 was created from a LinearGradientBrush: Dim mybrush As LinearGradientBrush = New LinearGradientBrush(_ Me.ClientRectangle, _ Color.Empty, _ Color.Empty, _ 45) Dim blend As ColorBlend = New ColorBlend() blend.Colors = New Color() { Color.Red, Color.Green, Color.Blue } blend.Positions = New Single() { 0.0F, 0.5F, 1.0F } mybrush.InterpolationColors = blend Dim mypen As Pen = New Pen(mybrush) ... mypen.Dispose() mybrush.Dispose() The ability to create a pen from a brush lets you use any effect you can create using the multitude of brushes provided by System.Drawing. |