Meherchilakalapudi.. writes for u….

Just another WordPress.com weblog

Archive for the ‘Graphic Application using with .Net’ Category

Graphic Application using with .Net

Posted by meherchilakalapudi on March 17, 2009

Work with GDI+ Brushes

In the previous sample, we showed how .NET gives us easy access to GDI+ via a comprehensive set of classes and methods from the System.Drawing namespace. You saw that to draw you need a Graphics object and a Pen. In this sample, we’ll show you how to use the Brush object to fill closed shapes such as rectangles and ellipses.

New Concepts

Whereas you use a pen to draw the outline of a shape, you use a brush to fill the interior of such a shape. You can choose to have both a border and a fill or to have only one of the two. To fill a shape, you need a Graphics object and a Brush. The Graphics object provides methods such as FillRectangle and FillEllipse, while the Brush gives you a way to specify the color, pattern, and other properties of the fill.

Five kinds of brushes are available, two in the System.Drawing namespace and three in System.Drawing.Drawing2D. They are as follows:

·         System.Drawing.SolidBrush  A brush composed of a single color. Use this brush to fill a shape with a solid color.

·         System.Drawing.TextureBrush  A brush that uses an image as its source. Use this brush to fill a shape from an image. For example, you might fill an oval shape from a portrait to imitate the look of an old-fashioned picture.

·         System.Drawing.Drawing2D.HatchBrush  A rectangular brush composed of three elements: a foreground color, which specifies the color of the lines in the hatch; a background color, which represents the color of the spaces between the lines; and a hatch pattern, which includes such choices as checkerboards, diagonals, diamonds, and zigzags.

·         System.Drawing.Drawing2D.LinearGradientBrush  A brush that lets you create a fill that transitions smoothly from one color to another. The Blend property of the brush lets you determine the relative intensity of the colors at each point along the gradient.

·         System.Drawing.Drawing2D.PathGradientBrush  Whereas the LinearGradient color transition goes from one edge of the shape to the other, the shading with this brush starts in the center of the shape and transitions to the outside. It can be used on simple shapes such as rectangles and ellipses, as well as on the more complex shapes represented by GraphicsPath objects.

Figure 10-2 shows a TextureBrush being used to fill a pair of ellipses using a photograph as its drawing source.


Figure 10-2: A TextureBrush lets you use a graphical image as the source for your drawing.

Code Walkthrough

The RedrawPicture procedure provides the meat of the demonstration. It creates one of the five types of brushes and assigns the appropriate user-defined properties to the brush. The brush is then assigned to m_Brush, which is used to draw one of three different shapes. There is also code to ensure that the user interface (UI) displays only the options that are appropriate for the type of brush being used. As you’ll notice, this procedure handles virtually all events fired by the UI.

Private Sub RedrawPicture(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Activated, _
    cboBrushType.SelectedIndexChanged, cboDrawing.SelectedIndexChang ed, _
    txtColor1.TextChanged, cboWrapMode.SelectedIndexChanged, _
    cboHatchStyle.SelectedIndexChanged, txtColor2.TextChanged, _
    cboGradientMode.SelectedIndexChanged, nudRotation.ValueChanged,  _
    nudGradientBlend.ValueChanged, MyBase.Resize

After clearing the picture box and the status bar, we’re ready to get to the business of creating a brush.

SolidBrush

The first option is a solid brush, based on the user-selected color. We’ll assign it to m_brush, our class-level brush variable, because doing that will make IntelliSense help available on the brush object.

    Select Case cboBrushType.Text
        Case "Solid"
            

            Dim mySolidBrush As New SolidBrush(m_Color1)
            m_Brush = mySolidBrush

HatchBrush

The second option is to create a new HatchBrush using the user-selected colors for foreground and background color settings. Because the HatchStyle property is read- only, it must be set as we instantiate the HatchBrush.

        Case "Hatch"
            

            Dim myHatchBrush As New HatchBrush( _
                CType(cboHatchStyle.SelectedItem, HatchStyle), _
                m_Color1, m_Color2)
            m_Brush = myHatchBrush

TextureBrush

The third option is to create a new TextureBrush based on a bitmap picture of water lilies. The bitmap can also be a pattern you’ve created. The WrapMode determines how the brush will be tiled if it’s not spread over the entire graphics area. The RotateTransform method rotates the brush by the user-specified amount. We could also have used a ScaleTransform to re-shape the brush. For example, this statement cuts the width of the brush in half and doubles the height: myTextureBrush.ScaleTransform(0.5F, 2.0F)

 

Caution 

Be cautious when creating a TextureBrush. If you define a Rectangle larger than the bitmap, it will trigger an OutOfMemory exception.

        Case "Texture"
            

            Dim myTextureBrush As New TextureBrush( _
                New Bitmap("..\WaterLilies.jpg"), m_BrushSize)
            myTextureBrush.WrapMode = CType(cboWrapMode.SelectedItem , _
                WrapMode)
            myTextureBrush.RotateTransform(nudRotation.Value)
            m_Brush = myTextureBrush

LinearGradientBrush

Our next option is to create a new LinearGradientBrush. The brush is based on a size defined by a rectangle. In this case, we’re using the user-defined m_BrushSize. Two colors are used, one for defining the start color of the gradient and one for defining the end color.

 

Tip 

You can create more advanced gradients by using the Blend property, which lets you specify the relative intensity of each of the colors along the gradient path.

We define the LinearGradientMode in the constructor. This controls the direction of the gradient. We could have used an angle, but for simplicity that isn’t done here. The WrapMode determines how the gradient will be tiled if it is not spread over the entire graphics area. The LinearGradientBrush can use all values for WrapMode except Clamp.

To set the point where the blending will focus, you can use any value between 0 and 1. The default is 1.

        Case "LinearGradient"
            

            Dim myLinearGradientBrush As New LinearGradientBrush( _
                m_BrushSize, m_Color1, m_Color2, _
                CType(cboGradientMode.SelectedItem, LinearGradient Mode))
            If CType(cboWrapMode.SelectedItem, WrapMode) <> _
                WrapMode.Clamp Then
                myLinearGradientBrush.WrapMode = _
                    CType(cboWrapMode.SelectedItem, WrapMode)
            Else
                Me.sbrDrawingStatus.Text += _
                    "A Linear Gradient Brush cannot use the " & _
                    "Clamp WrapMode."
            End If
            myLinearGradientBrush.RotateTransform(nudRotation.Valu e)
            myLinearGradientBrush.SetBlendTriangularShape( _
                nudGradientBlend.Value)
            m_Brush = myLinearGradientBrush

For more advanced uses, you can use the SetSigmaBellShape method to set where the center of the gradient occurs, as in this line of code: myLinearGradientBrush.SetSigmaBellShape(0.2)

PathGradient

The last option lets us create a path by defining a set of points and then follow that path by using PathGradient. In cases like this, you’ll often define and use a GraphicsPath object instead of a set of points, but in this case, we’re using a simple triangle. Once we’ve defined the triangle, we create a new PathGradientBrush based on the path just created. Anything not bounded by the path will be transparent instead of containing coloring.

The colors for the PathGradient are defined differently than other gradients because we can use different colors for each side. In this case, we’re using only one color, but we could assign a different color to each side of the path. The CenterColor is the color that the edges blend into. SurroundColors is an array of colors that defines the colors around the edge. We could also set the CenterPoint property somewhere other than the center of the path (even outside the rectangle bounding the path)—for example: myPathGradientBrush.CenterPoint = New PointF(50, 50)

        Case "PathGradient"
            

            Dim pathPoint() As Point = {New Point(0, m_BrushSize.Hei ght), _
                New Point(m_BrushSize.Width, m_BrushSize.Height), _
                New Point(m_BrushSize.Width, 0)}
            Dim myPathGradientBrush As New PathGradientBrush(pathPoi nt)
            myPathGradientBrush.CenterColor = m_Color1
            myPathGradientBrush.SurroundColors = New Color() {m_Co lor2}
            myPathGradientBrush.WrapMode = _
                CType(cboWrapMode.SelectedItem, WrapMode)
            myPathGradientBrush.RotateTransform(nudRotation.Value)
            myPathGradientBrush.SetBlendTriangularShape( _
                nudGradientBlend.Value)
            m_Brush = myPathGradientBrush

Conclusion

In this sample application, we’ve shown you how to use a Brush to fill the interiors of shapes. A HatchBrush is a brush with a variety of possible patterns. A TextureBrush is one whose source is an image. Gradient brushes let you create fills that transition smoothly from one color to another.

 

gditext

 

 

Work with GDI+ Text

You might find it surprising that to display text on a form, PictureBox, or some other surface you need to draw it—but that’s exactly the way it is. This sample shows some of the many features available when using GDI+ to work with text.

 

 

New Concepts

Fonts and text are rendered by means of GDI+, and the major method you’ll use to display text to the screen is the DrawString method of the Graphics object. You need four essential items to display text using DrawString:

·         The text to be displayed

·         A font in which you want the text displayed

·         A brush to draw with

·         The location where it should be displayed, which can be x and y coordinates or a rectangle within which the text will be displayed

Whereas you use a Pen to draw lines and curves, you need a Brush to draw text. As always, the Brush is a holder for characteristics of the drawing, while the Graphics object provides the methods for doing the drawing.

You have a variety of options when displaying text. You always display text within a bounding rectangle, within which you can use hatch brushes, texture brushes, gradient brushes, and of course, solid brushes. You must carefully measure the size of the text you intend to display, because the size of the bounding rectangle will determine whether the text is appropriately positioned on the screen and whether it will fit in the space provided. Figure 10-3 shows text reflected by the TranslateTransform method.


When you draw text with GDI+ brushes, you can use
transformations to show the text in a variety of orientations.

Code Walkthrough

We’ll examine several options for displaying text on a drawing surface, illustrating the point we made earlier—that text must be drawn.

Block Text

Our first option creates the sample text with a block appearance. We begin by setting up the brushes we’ll use for the foreground and background. We need a font object for the text, so we create one, using the values the user has set. Notice how we’ve used the CheckState of the chkBold check box. CheckState.Checked is equal to 1 and Unchecked is 0. FontStyle.Bold is equal to 1 and Regular is 0. So we simply cast the check state of the check box to a font style and we’ve declared whether we want boldface type or not. Then we create a Graphics object from the picture box and clear the box.

Private Sub btnBlockText_Click(...
    Dim myForeBrush As Brush = Brushes.Aquamarine
    Dim myBackBrush As Brush = Brushes.Black
    Dim myStyle As FontStyle = CType(chkBold.CheckState, FontStyle)
    Dim myFont As New Font("Times New Roman", Me.nudFontSize.Value,  _
        myStyle)
    Dim g As Graphics = picDemoArea.CreateGraphics()
    g.Clear(Color.White)

We need to determine the size that will be required to draw the sample text. The MeasureString method provides the exact dimensions of the provided string, so you can determine whether it will fit in the area where you want to display it. MeasureString returns its results in a SizeF structure, which has Width and Height properties. Using those properties, we calculate the x- and y-coordinates for the starting point of our display.

    Dim textSize As SizeF = g.MeasureString(Me.txtSample.Text, myFo nt)
    Dim xLocation As Single = (picDemoArea.Width - textSize.Width) /  2
    Dim yLocation As Single = (picDemoArea.Height -  textSize.Height) / 3

Now we’re ready to display the text. We’ll draw the black background first. To get the block effect, we draw the text repeatedly from the offset in the lower left up to the point where the main text will be drawn. Because people tend to think of light as coming from the upper left, we’ve subtracted the offset depth from the x dimension instead of adding it. If we had added it, the block might look more like a shadow.

    Dim i As Integer
    For i = CInt(nudBlockDepth.Value) To 0 Step -1
        g.DrawString(txtSample.Text, myFont, myBackBrush, _
            xLocation - i, yLocation + i)
    Next

Finally, we draw the white main text over the black text. Note how we provide the DrawString method with the text we want to display, the font to use, the brush to draw with, and the coordinates at which to draw.

    g.DrawString(txtSample.Text, myFont, myForeBrush, xLocation,  _
        yLocation)
End Sub

Brush Text

Here we’ll display the sample text by using either a Hatch or Gradient brush. In creating a HatchBrush, we must specify the style of hatch we want (in this case, Diagonal Brick), as well as foreground and background colors. Before we create the LinearGradientBrush, we want to provide a boundary within which the gradient will be displayed. So we create a rectangle that’s the size of our text. Then we pass that rectangle as the first argument to the constructor of the LinearGradientBrush, along with the starting and ending colors of the gradient and the orientation of the gradient (in this case, Forward Diagonal).

Private Sub btnBrushText_Click(...
    

    Dim textSize As SizeF = g.MeasureString(Me.txtSample.Text, myFont )
    Dim myBrush As Brush

    If Me.optHatch.Checked Then
        myBrush = New HatchBrush(HatchStyle.DiagonalBrick, _
            Color.Yellow, Color.Blue)
    Else
        Dim gradientRectangle As New RectangleF(New PointF(0, 0), t extSize)
        myBrush = New LinearGradientBrush(gradientRectangle, Col or.Blue, _
            Color.Yellow, LinearGradientMode.ForwardDiagonal)
    End If
    g.DrawString(txtSample.Text, myFont, myBrush, _
        (picDemoArea.Width - textSize.Width) / 2, _
        (picDemoArea.Height - textSize.Height) / 3)
End Sub

Embossed Text

The following procedure creates the sample text with an embossed look. To create the effect, the sample text is drawn twice. It’s drawn first in black, offset slightly from the drawing starting point, and then drawn again in white, the current background color. This gives the impression that the text is raised. To give the impression of engraving instead of embossing, simply use the negative of the offset value.

Private Sub btnEmboss_Click(...
    Dim myBackBrush As Brush = Brushes.Black
    Dim myForeBrush As Brush = Brushes.White
    

    Dim textSize As SizeF = g.MeasureString(Me.txtSample.Text, myFon t)
    xLocation = (picDemoArea.Width - textSize.Width) / 2
    yLocation = (picDemoArea.Height - textSize.Height) / 3

We’ll draw the black background first. (Note: if you subtract the nudEmbossDepth value instead of adding it, you’ll get an Engraved effect. Try it with the Depth control on the form.) Finally, we draw the white main text over the black text.

    g.DrawString(txtSample.Text, myFont, myBackBrush, _
            xLocation + Me.nudEmbossDepth.Value, _
            yLocation + Me.nudEmbossDepth.Value)
    g.DrawString(txtSample.Text, myFont, myForeBrush, xLocation, yLo cation)
End Sub

Reflected Text

This example reflects text around the baseline of the characters. It’s more advanced than most of the other examples and requires careful measurement of the text. Because we’ll be scaling, and scaling effects the entire Graphics object not just the text, we need to reposition the origin of the Graphics object from (0,0) to the (xLocation, yLocation) point. If we don’t, when we attempt to flip the text with a scaling transform, it will merely draw the reflected text at (xLocation, -yLocation), which is outside the viewable area.

Private Sub btnReflectedText_Click(...
    Dim myBackBrush As Brush = Brushes.Gray
    Dim myForeBrush As Brush = Brushes.Black
    

    g.TranslateTransform(xLocation, yLocation)

Reflecting around the origin still poses problems. The origin represents the upper left corner of the text’s bounding rectangle. This means the reflection will occur at the top of the original drawing. This is not how people are used to seeing reflected text. So we need to determine where to draw the text, and we can do that only when we’ve calculated the height required by the drawing.

This is not as simple as it might seem. The Height returned from the MeasureString method includes some extra spacing for descenders and white space. But we want only the height from the baseline (which is the line on which all caps sit). Any characters with descenders drop below the baseline. To calculate the height above the baseline, we need to use the GetCellAscent method. Because GetCellAscent returns a Design Metric value, it must be converted to pixels and scaled for the font size.

    Dim lineAscent As Integer
    Dim lineSpacing As Integer
    Dim lineHeight As Single
    Dim textHeight As Single
    lineAscent = myFont.FontFamily.GetCellAscent(myFont.Style)
    lineSpacing = myFont.FontFamily.GetLineSpacing(myFont.Style)
    lineHeight = myFont.GetHeight(g)
    textHeight = lineHeight * lineAscent / lineSpacing

 

Tip 

Reflection looks best with characters that can be reflected over the baseline nicely—like capital letters. Characters with descenders look odd. To fix that, factor in the height of the descenders as you calculate the text height, and then you’ll reflect across the lowest descender height. Here’s how:

Dim lineDescent As Integer
lineDescent = myFont.FontFamily.GetCellDescent(myFont.Style)
textHeight = lineHeight * (lineAscent + lineDescent) / lineSpacing

We’ll draw the reflected text first so that we can demonstrate the use of the GraphicsState object. A GraphicsState object maintains the state of the Graphics object as it currently stands. You can then scale, resize, and otherwise transform the Graphics object. You can immediately go back to a previous state by using the Restore method of the Graphics object. Had we drawn the main one first, we would not have needed the Restore method or the GraphicsState object.

First we’ll save the graphics state so that we can restore it later. To draw the reflection, we’ll use the ScaleTransform method with a negative value. Using -1 will reflect the text with no distortion. Then we restore the previous state and draw the main text.

    Dim myState As GraphicsState = g.Save()

    g.ScaleTransform(1, -1.0F)
    g.DrawString(txtSample.Text, myFont, myBackBrush, 0, - textHeight)
    g.Restore(myState)

    g.DrawString(txtSample.Text, myFont, myForeBrush, 0, - textHeight)
End Sub

Shadowed Text

This example draws the sample text with a solid brush and a shadow. To create the shadow, the sample text is drawn twice. The first time it’s offset and drawn in gray, and then it’s drawn again normally in black.

Private Sub btnShadowText_Click(...
    Dim myShadowBrush As Brush = Brushes.Gray
    Dim myForeBrush As Brush = Brushes.Black
    

    g.DrawString(txtSample.Text, myFont, myShadowBrush, _
        xLocation + Me.nudShadowDepth.Value, _
        yLocation + Me.nudShadowDepth.Value)
    g.DrawString(txtSample.Text, myFont, myForeBrush, xLocation, yLo cation)
End Sub

Sheared Text

The following procedure shears the text so that it appears angled. This requires the use of a Matrix, which will define the shear. Because we’ll be scaling, and scaling affects the entire Graphics object and not just the text, we need to reposition the origin of the Graphics object from (0, 0) to the (xLocation, yLocation) point.

Private Sub btnShearText_Click(...
    

    g.TranslateTransform(xLocation, yLocation)

Now we set a reference to the Transform object for the current Graphics object, Shear it by the specified amount, and finally, draw the main text.

    Dim myTransform As Matrix = g.Transform
    myTransform.Shear(nudSkew.Value, 0)
    g.Transform = myTransform
    g.DrawString(txtSample.Text, myFont, myForeBrush, 0, 0)
End Sub

Simple Text

The following procedure simply takes the lines of text in the text box and places them in the picDemoArea PictureBox. The text will word wrap as necessary, but it will not scroll.

Private Sub btnSimpleText_Click(...
    

    g.DrawString(txtLongText.Text, myFont, myForeBrush, _
        New RectangleF(0, 0, picDemoArea.Width, picDemoArea.Height))
End Sub

Conclusion

In this sample application, you’ve seen that you display text within a bounding rectangle, whose size you must measure with the MeasureString method to determine whether the text is appropriately positioned on the screen and whether it will fit in the space provided. You’ve also seen that you can use any font on your system when you’re drawing text. Powerful scaling and transformation methods let you twist, bend, shear, and distort text into a variety of shapes.

 

manipulateimages

 

                

 

Work with GDI+ to Manipulate Images

This sample shows you how to manipulate images using GDI+, including changing the size of an image and rotating, zooming, and cropping an image.

 

 

New Concepts

GDI+ provides new ways to work with images. This example illustrates several of them.

Changing the Size of an Image

Once you’ve loaded your image into a PictureBox, you can apply one of four SizeMode settings to the PictureBox:

·         AutoSize

·         The PictureBox size is adjusted to match the size of the image in it.

·         CenterImage

·         The image is displayed in the center of the PictureBox. If the image is larger than the PictureBox, its outside edges are clipped.

·         Normal

·         The image is located in the upper left corner of the PictureBox. Its right and bottom edges are clipped if it is larger than the PictureBox.

·         StretchImage

·         The opposite of AutoSize. The image is stretched or shrunk to fit the size of the PictureBox.

Rotating Images

The Image class RotateFlip method lets you rotate an image, flip it, or both. You provide the method with a RotateFlipType argument (for example, Rotate180FlipX) that determines how many degrees you want to rotate the image and the axis, if any, on which you want to flip it. Choices include rotation by 90, 180, and 270 degrees; flipping on the x-axis, y-axis, or both; and combinations of all these options. Rotation is always clockwise.

Zooming and Cropping Images

Zooming means resizing the image so that it appears that the user’s perspective has changed by being either brought closer to or moved farther away from the picture.

Cropping consists of creating a new image at a size you specify and filling it with the portion of the old image that will fit the dimensions of the new one. If you want to allow the user to undo the crop, you need to create a variable that holds a copy of the bitmap before it’s cropped. Figure 10-4 shows an image that is both rotated and cropped.

The WaterLilies.jpg, which is bigger than the PictureBox, is centered within it, and its edges are cropped. The inset shows a portion of the image after it is rotated by 90 degrees.

Code Walkthrough

Let’s examine some ways we can manipulate an image, starting with resizing it.

Image Sizing

All four size settings mentioned previously are represented by four Size Mode radio buttons on the sample form. The SizeModeRadioButtons_CheckedChanged procedure handles the CheckedChanged events of all four radio buttons, and it adjusts the PictureBox.SizeMode property accordingly. Each radio button stores one of the values of the PictureBoxSizeMode enumeration (whose values are 0 through 3) in its Tag property.

Private Sub SizeModeRadioButtons_CheckedChanged(...
    Dim opt As RadioButton = CType(sender, RadioButton)
    Dim sm As PictureBoxSizeMode = CType(opt.Tag, PictureBoxSizeMode )
    If opt.Checked Then
        picImage.SizeMode = sm

You must manually reset the PictureBox to its original size if AutoSize has been set. It will not automatically return to the size set in the designer.

        If sm = PictureBoxSizeMode.AutoSize Then
            btnFit.Enabled = False
        Else
            btnFit.Enabled = True
            picImage.Width = PICTUREBOX_WIDTH



            picImage.Height = PICTUREBOX_HEIGHT

        End If
    End If
End Sub

The Fit procedure makes the image fit properly in the PictureBox. You might think that AutoSize would make the image appear in the PictureBox according to its true aspect ratio within the fixed bounds of the PictureBox. But AutoSize simply expands or shrinks the PictureBox itself. So the Fit procedure determines whether the image is smaller than the PictureBox. If it is, and if Fit was called by the Zoom In button, it centers the image.

Private Sub Fit()
    If picImage.Image.Width < picImage.Width And _
        picImage.Image.Height < picImage.Height Then
        If Not IsFitForZoomIn Then
            picImage.SizeMode = PictureBoxSizeMode.CenterImage
        End If
    End If
    CalculateAspectRatioAndSetDimensions()
End Sub

Image Rotation

As we mentioned earlier, rotation is always clockwise when you use the RotateFlip method, and in btnRotateRight_Click in the following code, we’re simply rotating the image by 90 degrees. In btnRotateLeft_Click, we’re achieving the left rotation by rotating the image by 270 degrees, which is the same as if we had rotated it counter- clockwise by 90 degrees. Note that in each case we need to refresh the PictureBox after the rotation.

Private Sub btnRotateRight_Click(...
    picImage.Image.RotateFlip(RotateFlipType.Rotate90FlipNone)

    picImage.Refresh()
End Sub



Private Sub btnRotateLeft_Click(...
    picImage.Image.RotateFlip(RotateFlipType.Rotate270FlipNone)

    picImage.Refresh()
End Sub

Zooming

To zoom, we resize the image, as shown in the following procedure. When zooming in or out, the SizeMode controls on the sample form are disabled. Otherwise, the zooming won’t work as anticipated. The following If test ensures that the initial Zoom In transition is smooth. Without the test, if the SizeMode is something other than AutoSize, the image can appear to Zoom Out on the first click, and then Zoom In on subsequent clicks.

Private Sub btnZoomIn_Click(...
    If grpSizeMode.Enabled Then
        picImage.SizeMode = PictureBoxSizeMode.AutoSize

    End If
    grpSizeMode.Enabled = False
    btnFit.Enabled = True
    IsFitForZoomIn = True

The StretchImage mode works best for zooming because the image is forced to conform to the size of the PictureBox. Zoom works best if you first fit the image according to its true aspect ratio. (See CalculateAspectRatioAndSetDimensions.)

When it’s time to actually do the zoom, you do it by simply adjusting the image’s dimensions up or down—in this case, by 25 percent. You could, of course, choose any increment you want, including letting the user enter it.

    picImage.SizeMode = PictureBoxSizeMode.StretchImage
    Fit()
    picImage.Width = CInt(picImage.Width * 1.25)
    picImage.Height = CInt(picImage.Height * 1.25)
End Sub



Private Sub btnZoomOut_Click(...
    grpSizeMode.Enabled = False
    btnFit.Enabled = True
    Fit()
    picImage.SizeMode = PictureBoxSizeMode.StretchImage
    picImage.Width = CInt(picImage.Width / 1.25)
    picImage.Height = CInt(picImage.Height / 1.25)
End Sub

The following procedure calculates and returns the image’s aspect ratio and sets its proper dimensions. It’s used by the Fit procedure and also for saving thumbnails of images.

Private Function CalculateAspectRatioAndSetDimensions() As Double
    Dim ratio As Double
    If picImage.Image.Width > picImage.Image.Height Then
        ratio = picImage.Image.Width / _
                picImage.Image.Height
        picImage.Height = CInt(CDbl(picImage.Width) / ratio)

    Else
        ratio = picImage.Image.Height / _
                picImage.Image.Width
        picImage.Width = CInt(CDbl(picImage.Height) / ratio)

    End If
    Return ratio
End Function

Cropping

In our example, we accept upper left x- and y-coordinates from the user, along with the width and height of the desired new image. Then we create a rectangle defined by those coordinates (relative to the upper left corner of the PictureBox) and the desired width and height.

Private Sub btnCrop_Click(...
    If IsValidCropValues() Then
        imgUndo = picImage.Image
        btnUndo.Enabled = True
        Dim recSource As New Rectangle(CInt(txtXCoord.Text), _
            CInt(txtYCoord.Text), CInt(txtWidth.Text), _
            CInt(txtHeight.Text))

 

Caution 

You might be tempted to create a Graphics object off the PictureBox (rather than a new Bitmap) and then to clear the PictureBox and draw the cropped image onto it, like this:

Dim grPicImage As Graphics = picImage.CreateGraphics
grPicImage.Clear(picImage.BackColor)
grPicImage.DrawImage(picImage.Image, 0, 0, recSource, _
    GraphicsUnit.Pixel)

This will appear to work, but as soon as you use any of the other controls on the form you’ll see that the PictureBox actually still contains the original image, not the cropped one.

Then we create a new, blank Bitmap on which we will draw the cropped image. We get a Graphics object from the Bitmap for drawing, and we draw the image in the upper left corner of the Bitmap. Finally, we set the PictureBox image to the new cropped image.

        Dim bmpCropped As New Bitmap(CInt(txtWidth.Text), _
            CInt(txtHeight.Text))
        Dim grBitmap As Graphics = Graphics.FromImage(bmpCropped)
        grBitmap.DrawImage(picImage.Image, 0, 0, recSource, _
            GraphicsUnit.Pixel)
        picImage.Image = bmpCropped
    End If
End Sub

Conclusion

In this sample application, we’ve shown you that an image in a PictureBox can be resized several ways by setting the SizeMode property of the PictureBox. You’ve seen that you can rotate and flip an image at the same time, and the RotateFlip method accepts a variety of RotateFlipType arguments that let you turn and flip an image in any direction you want. Zooming means resizing the image. Be careful about the first user click after choosing Zoom In if the SizeMode is something other than AutoSize.

 

screensaverwithgdi

 

 

 

Application #89: Create a Screen Saver with GDI+

You might think there’s something mysterious about a Microsoft Windows screen saver, but it’s really nothing more than a Windows application in disguise. You can easily build your own screen saver by creating a regular Windows application with a form that’s displayed in dialog mode, maximized to fill the screen, and waiting for a cue to be dismissed. The cue can be a mouse click or a movement of the mouse. This sample shows you how to create and deploy a screen saver with Visual Basic .NET.

Building Upon…

Application #82: Serialize Objects

Application #85: Work with GDI+ Pens

Application #86: Work with GDI+ Brushes

Application #88: Work with GDI+ to Manipulate Images

 

New Concepts

You’ll want your screen saver form not to have a title bar, not to show up in the taskbar, and not to be “touchable” in any way by the user (except to dismiss it with a mouse click or a mouse movement). So you’ll need to set the following form properties:

·         FormBorderStyle = None

·         ControlBox = False

·         MaximizeBox = False

·         MinimizeBox = False

·         ShowInTaskbar = False

·         SizeGripStyle = Hide

·         TopMost = True

·         WindowState = Maximized

 

Tip 

While you’re developing the screen saver, set the form’s size to a fraction of the screen’s size, set the ControlBox property to True, and set the WindowState property to Normal. That way the form won’t cover the entire screen, you can see your code for debugging purposes, and the form will have a title bar so that you can move it around.

Code Walkthrough

The sample solution has two projects: Create a Screensaver with GDI+.vbproj and GDI+ Screen Saver.vbproj. The second one is the screen-saver project, while the first is a small installation program to install your screen saver once you’ve completed it. The procedures we’ll describe next are in the screen-saver project, except for those in the “Deploying the Screen Saver” section. Figure 10-5 shows the screen saver form during development, small and sizeable.


Figure 10-5: While you’re developing your screen saver, keep the form small and sizeable so that you can test and debug it easily.

Application Entry Point

Sub Main is the entry point into our application, the first procedure that executes when the screen-saver program is run. The STAThread attribute means this application will run in a single-threaded apartment, and it’s needed for COM interoperability reasons. Windows will pass parameters to this program whenever a user is setting up the screen saver using the Display Properties | Screen Saver property screen. The parameters will be passed in an array named args, and Windows will pass a /p, /c, or /s argument, depending on how the screen saver should behave. We’ll explain each argument in the following paragraphs.

First we need to determine whether an argument was passed and, if so, which one. A /p argument means we’re being asked to show a preview of the screen saver. We haven’t implemented the preview functionality here because it involves creating and joining threads and is beyond the scope of this sample, so we’ll simply exit the application.

<STAThread()> Shared Sub Main(ByVal args As String())
    If args.Length > 0 Then
        If args(0).ToLower = "/p" Then
            Application.Exit()
        End If

If the screen saver should offer a form for user options, Windows passes a /c, so we’ll create and display a frmOptions form, and then exit when the form is closed. If the screen saver should simply execute normally, Windows passes a /s, so we’ll create and display a screenSaverForm and then exit when the form is closed.

        If args(0).ToLower.Trim().Substring(0, 2) = "/c" Then
            Dim userOptionsForm As New frmOptions()
            userOptionsForm.ShowDialog()

            Application.Exit()
        End If
        If args(0).ToLower = "/s" Then
            Dim screenSaverForm As New frmSceenSaver()
            screenSaverForm.ShowDialog()

            Application.Exit()
        End If

If there are no arguments, we’ll simply execute the screen saver normally, because it means that the user double-clicked the .scr or .exe file or ran it from within Visual Studio. We know this because otherwise Windows would have passed a parameter to the application.

    Else
        Dim screenSaverForm As New frmSceenSaver()
        screenSaverForm.ShowDialog()

        Application.Exit()
    End If
End Sub

Normal Operation

When the screen-saver form is loaded, we initialize it by creating the Graphics object we’ll use for drawing, loading the user’s saved options (creating an options file if one does not exist), setting the speed based on the user-defined options, and enabling the timer. The speed setting dictates how quickly shapes will be displayed when the screen saver is active.

Private Sub frmSceenSaver_Load(...
    m_Graphics = Me.CreateGraphics()
    m_Options.LoadOptions()

    Select Case m_Options.Speed
        Case "Slow"
 

            Me.tmrUpdateScreen.Interval = 500
        Case "Fast"

            Me.tmrUpdateScreen.Interval = 100
        Case Else
            Me.tmrUpdateScreen.Interval = 200
    End Select
    Me.tmrUpdateScreen.Enabled = True
End Sub

All subsequent operations are in response to events. When the timer ticks, a new shape is drawn on the screen, and this continues until a mouse button is clicked or the mouse is moved over the form.

Private Sub tmrUpdateScreen_Tick(...
    DrawShape()

End Sub

The DrawShape subroutine draws a randomly colored, randomly sized shape to the screen, based on some user-defined parameters. It starts by computing the largest possible values for the screen, as indicated by the form width and height. (Remember that the form is maximized.) Note that x1, x2, y1, and y2 are coordinates for random points to be generated later, myRect is the rectangle within which the shapes will be drawn, and myColor is the color to be used to draw the shapes.

Private Sub DrawShape()
    Dim maxX As Integer = Me.Width

    Dim maxY As Integer = Me.Height

    Dim x1, x2, y1, y2 As Integer
    Dim myRect As Rectangle
    Dim myColor As Color

Next we generate some random numbers ranging between zero and the maximums we determined earlier, and we create a rectangle based on the generated coordinates.

    x1 = m_Random.Next(0, maxX)
    x2 = m_Random.Next(0, maxX)
    y1 = m_Random.Next(0, maxY)
    y2 = m_Random.Next(0, maxY)
    myRect = New Rectangle(Math.Min(x1, x2), Math.Min(y1, y2), _
        Math.Abs(x1 - x2), Math.Abs(y1 - y2))

We’ll select a color at random for the shape we’re about to draw. If the user wants transparent shapes, we’ll allow the transparency to be randomly generated as well. If not, we’ll set the Alpha to 255 (the maximum). Alpha, which is the first argument to the Color.FromArgb function, determines how opaque the shape will be.

    If m_Options.IsTransparent Then
        myColor = Color.FromArgb(m_Random.Next(255), m_Random.Ne xt(255), _
            m_Random.Next(255), m_Random.Next(255))
    Else
        myColor = Color.FromArgb(255, m_Random.Next(255), _
            m_Random.Next(255), m_Random.Next(255))
    End If

Finally, we draw an ellipse or rectangle based on user-defined options.

    If m_Options.Shape = "Ellipses" Then
        m_Graphics.FillEllipse(New SolidBrush(myColor), myRect)
    Else
        m_Graphics.FillRectangle(New SolidBrush(myColor), myRect)
    End If
End Sub

Ending the Application

When the user clicks a button or moves the mouse, we want the screen saver to quit. The following routines accomplish that. The first one simply exits if any mouse button is clicked on the screen saver form.

Private Sub frmSceenSaver_MouseDown(...
    Application.Exit()

End Sub

The second one responds to a mouse movement. Because the MouseMove event can sometimes be fired by very trivial moves of the mouse, we’ll verify that the mouse has actually been moved by at least a few pixels before exiting. To do that, we store the current location of the mouse, turn on a switch that shows we’re tracking the mouse movements, and exit only if the mouse has been moved at least 10 pixels.

Private Sub frmSceenSaver_MouseMove(...
    If Not m_IsActive Then
        Me.m_MouseLocation = New Point(e.X, e.Y)
        m_IsActive = True
    Else
        If Math.Abs(e.X - Me.m_MouseLocation.X) > 10 Or _
            Math.Abs(e.Y - Me.m_MouseLocation.Y) > 10 Then
            Application.Exit()
        End If
    End If
End Sub

Setting Options

We want the user to be able to choose screen-saver options such as how fast shapes should be drawn on the screen, whether they should be rectangles or ellipses, and whether the shapes should be transparent. So we provide an Options form, which gets called when the screen-saver program is invoked with the /c argument.

We’ve defined a class named Options (which you can see in Options.vb) to hold the preferences as properties and provide methods for loading and saving the preferences. Creating a class makes it easy to save the options to disk in XML format by serializing the class and then later retrieving them by deserialization. (See “Application #82: Serialize Objects” for more details.)

The btnOK_Click procedure in frmOptions creates an Options object and sets its values to the user-selected values on the form. It then saves the choices to disk.

Private Sub btnOK_Click(...
    Dim myOptions As New Options()

    If Me.optEllipses.Checked Then
        myOptions.Shape = "Ellipses"
    Else
        myOptions.Shape = "Rectangles"
    End If
    myOptions.IsTransparent = Me.chkTransparent.Checked
    myOptions.Speed = Me.cboSpeed.Text
    myOptions.SaveOptions()

    Me.Close()
End Sub

The Form_Load event procedure of frmOptions loads the current user- defined options and sets the controls on the form accordingly. The Load method of the Options class always returns values, even if the options file doesn’t currently exist.

Private Sub frmOptions_Load(...
    Dim myOptions As New Options()

    myOptions.LoadOptions()
    Me.cboSpeed.Text = myOptions.Speed

    Me.chkTransparent.Checked = myOptions.IsTransparent

    If myOptions.Shape = "Ellipses" Then
        Me.optEllipses.Checked = True
    Else
        Me.optRectangles.Checked = True
    End If
End Sub

Deploying the Screen Saver

Screen savers live in the Windows System directory, which by default is C:\Windows\System32. To install your new screen saver, you simply have to copy the .exe application file to the System directory, changing the extension from .exe to .scr. In the sample solution, we’ve provided a project that copies the file to its correct location. It assumes you have a copy of “101 VB.NET Sample Applications Screensaver.scr” in the root directory of the 89 Create a Screensaver with GDI+ project.

If you don’t, copy “101 VB.NET Sample Applications Screensaver.exe” from GDI+ Screen Saver\bin to the root directory of the 89 Create a Screensaver with GDI+ project and change the extension to .scr.

Once you’re sure the screen-saver file is in place, the btnInstall_Click procedure of frmMain handles the installation for you. Notice the use of Environment.CurrentDirectory (the folder where the executable is running) and Environment.SystemDirectory (the Windows System directory).

Private Sub btnInstall_Click(...
    Dim fileName As String = _
        "101 VB.NET Sample Applications Screensaver.scr"
    Dim sourceFile As String = _
        Environment.CurrentDirectory & "\..\" & fileName
    Dim destFile As String = Environment.SystemDirectory & "\" & fileName
    Try
        File.Copy(sourceFile, destFile, True)
    Catch ex As Exception
        MsgBox(ex.ToString(), MsgBoxStyle.Exclamation, Me.Text)
    End Try
End Sub

Conclusion

In this sample application, you’ve seen that a screen saver is simply a Windows Application in disguise. Once you change its file extension from .exe to .scr and put it in the Windows System directory, it will show up on the list of screen savers in Control Panel | Display. You’ve seen that Windows passes /p, /c, or /s arguments to the screen saver to indicate how it should behave. They indicate Preview, Set Options, and Normal, respectively. We’ve also shown you that generating random colors and even random levels of transparency is easy with the Random class and the FromArgb function of the Color class.

 

animationwithgdi1

 

ApplicationAnimation

This application demonstrates how to do animation with GDI+, including classic frame animation, drawing and moving a shape on the screen, and animating text with a gradient fill. The sample form offers three animations: a winking eye, a bouncing ball, and a text animation. (The text animation is shown in Figure 10-6.)

 


Figure 10-6: Creating an animated gradient in text or any other shape is easy—you simply change its point of origin in a loop.

New Concepts

Once you know how to manipulate images with the Framework GID-related classes, you’ll probably want to make some of them come alive with animation. With a few carefully chosen methods and some basic mathematical skills, you can bring an image to life. We’ll demonstrate the principles in the code walkthrough.

Code Walkthrough

The animations shown here require a timer whose TimerOnTick procedure triggers the image’s movement. Before starting the motion, however, some initial preparation is required, including clearing existing drawings when switching from one animation to another, computing the size of the bouncing ball, and so on.

Setting Up

The OnResize procedure fills the bill in handling the setup chores. This method overrides the OnResize method in the base Control class. OnResize raises the Resize event, which occurs when the control (in this case, the Form) is resized. That means it will be called when the form is loaded because the Resize event fires as a part of that process. We will also call this procedure whenever a radio button is clicked to change animations.

First, if Wink is chosen, we’ll simply clear the form.

 

Tip 

To clear the form, you could also use grfx.Clear(Me.BackColor) or Me.Invalidate().

Protected Overrides Sub OnResize(ByVal ea As EventArgs)
    If optWink.Checked Then
        Dim grfx As Graphics = CreateGraphics()
        Me.Refresh()
        grfx.Dispose()

If the user selects the Bouncing Ball, we have much more to do. First we erase any existing drawings. Next, we determine the size of the ball by setting the radius of the ball to a fraction of either the width or height of the client area, whichever is less. Then we set the width and height of the ball by multiplying the radius we calculated earlier by the horizontal and vertical resolution of the Graphics object.

    ElseIf optBall.Checked Then
        Dim grfx As Graphics = CreateGraphics()
        grfx.Clear(Me.BackColor)
        Dim dblRadius As Double = Math.Min(ClientSize.Width /  grfx.DpiX, _
            ClientSize.Height / grfx.DpiY) / intBallSize
        intBallRadiusX = CInt(dblRadius * grfx.DpiX)
        intBallRadiusY = CInt(dblRadius * grfx.DpiY)
        grfx.Dispose()

Now we’ll set the distance the ball moves to 1 pixel or a fraction of the ball’s size, whichever is greater. This means that the distance the ball moves each time it is drawn is proportional to its size, which is, in turn, proportional to the size of the client area. Thus, when the client area is shrunk, the ball slows down, and when it is increased, the ball speeds up—resulting in an apparent constant speed no matter what size the form is set to.

        intBallMoveX = CInt(Math.Max(1, intBallRadiusX /  intMoveSize))
        intBallMoveY = CInt(Math.Max(1, intBallRadiusY /  intMoveSize))

Notice that the value of the ball’s movement also serves as the margin around the ball, which determines the size of the actual bitmap on which the ball is drawn. So the distance the ball moves is exactly equal to the size of the bitmap, which permits the previous image of the ball to be erased before the next image is drawn—all without an inordinate amount of flickering. We determine the actual size of the Bitmap on which the ball is drawn by adding the margins to the ball’s dimensions.

        intBitmapWidthMargin = intBallMoveX
        intBitmapHeightMargin = intBallMoveY
        intBallBitmapWidth = 2 * (intBallRadiusX + intBitmapWidthMar gin)
        intBallBitmapHeight = 2 * (intBallRadiusY + intBitmapHeightM argin)

Now that we have the size of the ball, we create a new bitmap, passing in the dimensions we just calculated. Then we obtain the Graphics object exposed by the Bitmap, clear the existing ball, and draw the new ball. Finally, we reset the ball’s position to the center of the client area.

        myBitmap = New Bitmap(intBallBitmapWidth, intBallBitmapHeigh t)
        grfx = Graphics.FromImage(myBitmap)
        With grfx
            .Clear(Me.BackColor)
            .FillEllipse(Brushes.Red, New Rectangle(intBallMoveX, _
                intBallMoveY, 2 * intBallRadiusX, 2 * intBallRadiusY ))
            .Dispose()
        End With
        intBallPositionX = CInt(ClientSize.Width / 2)
        intBallPositionY = CInt(ClientSize.Height / 2)

In the final choice in the If statement, we simply clear the form if the user selects the Animated Text option.

    ElseIf optText.Checked Then
        Dim grfx As Graphics = CreateGraphics()
        grfx.Clear(Me.BackColor)
    End If
End Sub

Frame Animation

Now that the setup is done, the bulk of the work is passed to the following TimerOnTick procedure, which handles the Tick event for the Timer. This is where the animation takes place, so it’s the heart of our application.

The first option handles the winking eye. To create the illusion of winking, we’ll display a sequence of four images stored in an array, each one showing a different stage of the wink. We draw each image with the DrawImage method of a Graphics object, using overload #8, which takes the image to be displayed, the x- and y- coordinates (which in this case will center the image in the client area), and the width and height of the image. Note intCurrentImage, a class-level counter that begins at 1 and determines which of the four images in the array will be displayed as the animation progresses.

Protected Overridable Sub TimerOnTick(...
    If optWink.Checked Then
        Dim grfx As Graphics = CreateGraphics()
        Dim img As Image = arrImages(intCurrentImage)

        grfx.DrawImage(img, CInt((ClientSize.Width - img.Width) /  2), _
            CInt((ClientSize.Height - img.Height) / 2), img.Width, _
            img.Height)

Each time the timer ticks, we bump up the image counter intImageIncrement, which is a class-level variable that lets us control the animation order. When we get to the last image of the four, we reverse the order so that the eye closes, and when we get back to the first image, we reverse the animation order again so that the eye re- opens.

        intCurrentImage += intImageIncrement

        If intCurrentImage = 3 Then
            intImageIncrement = -1
        ElseIf intCurrentImage = 0 Then
            intImageIncrement = 1
        End If

Bouncing Ball

The next option enables the bouncing ball. First we create a Graphics object, and then we use it to draw the bitmap containing the ball on the Form. Then we increment the ball’s position by the distance it has moved in both the x and y directions after being redrawn.

    ElseIf optBall.Checked Then
        Dim grfx As Graphics = CreateGraphics()
        grfx.DrawImage(myBitmap, _
            CInt(intBallPositionX - intBallBitmapWidth / 2), _
            CInt(intBallPositionY - intBallBitmapHeight / 2), _
            intBallBitmapWidth, intBallBitmapHeight)
        grfx.Dispose()
        intBallPositionX += intBallMoveX



        intBallPositionY += intBallMoveY

 

Tip 

You should always call Dispose for objects that expose this method instead of waiting for the Garbage Collector to do it for you. This almost always increases your application’s performance.

When the ball hits a boundary, we want to reverse its direction. So when its x- axis position puts it beyond the width of the form, or less than zero, we invert intBallMoveX, which controls its direction. We do the same for the y-axis (inverting intBallMoveY), but we set the upper boundary at 40 instead of zero so that the ball doesn’t bounce into the controls at the top of the form.

        If intBallPositionX + intBallRadiusX >= ClientSize.Width _
            Or intBallPositionX - intBallRadiusX <= 0 Then
            intBallMoveX = -intBallMoveX

            Beep()
        End If
        If intBallPositionY + intBallRadiusY >= ClientSize.Height _
            Or intBallPositionY - intBallRadiusY <= 40 Then
            intBallMoveY = -intBallMoveY

            Beep()
        End If

Animated Text

For the final option, animating a gradient with text, we begin by setting the font type and the text to be displayed, and we determine the size of the text with the MeasureString method. Keep in mind that MeasureString returns its results in a SizeF structure, which has Width and Height properties. Using those properties, we calculate the x- and y-coordinates for the starting point of our display, which in this case is the center of the client area.

    ElseIf optText.Checked Then
        Dim grfx As Graphics = CreateGraphics()
        Dim font As New font("Microsoft Sans Serif", 96, _
            FontStyle.Bold, GraphicsUnit.Point)
        Dim strText As String = "GDI+!"
        Dim sizfText As New SizeF(grfx.MeasureString(strText, font) )

        Dim ptfTextStart As New PointF( _
            CSng(ClientSize.Width - sizfText.Width) / 2, _
            CSng(ClientSize.Height - sizfText.Height) / 2)

We’ll set the start point of the gradient to the upper left of the text’s boundary rectangle. We’ll set the end point’s x coordinate to a changing value so that we can achieve the animation effect. Once we’ve instantiated the LinearGradientBrush, we draw the text using Blue for the gradient’s starting color and use the form’s background color for the ending color.

        Dim ptfGradientStart As New PointF(0, 0)
        Dim ptfGradientEnd As New PointF(intCurrentGradientShift, 20 0)
        Dim grBrush As New LinearGradientBrush(ptfGradientStar t, _



            ptfGradientEnd, Color.Blue, Me.BackColor)

        grfx.DrawString(strText, font, grBrush, ptfTextStart)
        grfx.Dispose()

Now we’ll animate the gradient by moving its starting point based on the value in intCurrentGradientShift, which is incremented or decremented by intGradientStep each time this procedure gets called. Once intCurrentGradientShift gets to 500, we reverse its direction.

        intCurrentGradientShift += intGradientStep

        If intCurrentGradientShift = 500 Then
            intGradientStep = -5
        ElseIf intCurrentGradientShift = -50 Then
            intGradientStep = 5
        End If
    End If
End Sub

Conclusion

In this sample application, you’ve seen that you can achieve image animation just by drawing a sequence of images to the screen. You’ve also seen that you might sometimes need more complex computations when you need to precisely place an image in a series of progressive locations, as with our bouncing ball example. We showed you that to clear a form, you can use Graphics.Clear(Me.BackColor), Me.Refresh(), or Me.Invalidate(). And you saw that creating an animated gradient is easy. You simply change its origin in a loop.

 

Lesson 1: Using GDI+

Microsoft Windows is a graphical interface. Users interact with applications through windows, which are graphical representations of application data and options. To take advantage of the rich user experience that Windows offers, you should design your applications so that they expose their functionality to the user graphically. Although the .NET Framework provides a wide array of graphical controls and methods that allow you to create fully visual applications, you might want to render your own graphic content in your application or create a custom appearance for a control. To take full advantage of the features afforded by the Windows operating system, you must learn to use the Graphic Device Interface (GDI). In the .NET Framework, this interface is fully managed and is referred to as GDI+.



After this lesson, you will be able to

  • Describe how to create and use a Graphics object
  • Explain how to use pens, brushes, and colors
  • Describe how to draw an outlined shape and a filled shape
  • Describe how to render text
  • Describe how to create a GraphicsPath and a Region

Estimated lesson time: 40 minutes



GDI+ is the name given to the .NET Framework’s managed implementation of the GDI, which is used to display graphical information on the computer screen. This interface is wrapped into six different namespaces, which are broken down by related functionality.

The System.Drawing Namespaces

The array of functionality exposed by the System.Drawing namespaces is vast. Although an exhaustive dissection of these namespaces and their associated functionality is beyond the scope of this book, this lesson will familiarize you with the techniques involved in accessing the classes and methods they contain.

Table 7.1 summarizes the System.Drawing namespaces and the general functions of the classes they contain.

Table 7.1 The System.Drawing Namespaces

Namespace

Contains

System.Drawing Most of the classes actually involved in rendering graphical content to the screen. This namespace is the primary namespace used for graphics programming.
System.Drawing.Design Classes that are designed to extend the design time user interface.
System.Drawing.2D Classes that are designed to render advanced visual effects.
System.Drawing.Imaging Classes that allow advanced manipulation of image files.
System.Drawing.Printing Classes that facilitate printing content.
System.Drawing.Text Classes that facilitate advanced manipulation of fonts.

Almost all of the classes you will need to render graphics are provided in the System.Drawing namespace. For advanced rendering, you might need some classes in the System.Drawing.2D namespace, and to enable printing, you will need classes in the System.Drawing.Printing namespace.

The Graphics Object

The principal object used in rendering graphics is the Graphics object. The Graphics object is located in the System.Drawing namespace. A Graphics object represents the drawing surface of a visual element, such as a form, a control, or an Image object. Thus, a form has an associated Graphics object that can be used to draw inside the form; a control has an associated Graphics object that is used to draw inside the control, and so on. All of the actual rendering of visual elements is done by the Graphics object.

Because each Graphics object must be associated with a visual element, you cannot directly instantiate one with a call to the constructor. Instead, you must create a Graphics object directly from the visual element. Classes that inherit from Control (including Form) expose a CreateGraphics method that allows you to get a reference to the Graphics object associated with that control. The following example demonstrates how to access the Graphics object of a Form called myForm:

Visual Basic .NET

Dim myGraphics as System.Drawing.Graphics
myGraphics = myForm.CreateGraphics()

Visual C#

System.Drawing.Graphics myGraphics;
myGraphics = myForm.CreateGraphics();

The Graphics object thus created can then be used to render graphics on that form.

If you are working with images, you can use the Graphics.FromImage method to create a Graphics object associated with a particular Image. This method is a static method, so you do not need an active reference to a Graphics object to call it. The Image object can be any object that inherits from the Image class, such as an instance of Bitmap. The following example demonstrates how to create a Bitmap object from a file and then create an associated Graphics object:

Visual Basic .NET

Dim myImage As New Bitmap("C:\myImage.bmp")
Dim myGraphics As System.Drawing.Graphics
myGraphics = Graphics.FromImage(myImage)

Visual C#

Bitmap myImage = new Bitmap("C:\\myImage.bmp");
System.Drawing.Graphics myGraphics;
myGraphics = Graphics.FromImage(myImage);

Note that the Image does not have to currently be visible to create a Graphics object or perform manipulations with it.

Coordinates

Rendering takes place on the screen in the region set by the bounds of the control. This region is measured in two-dimensional coordinates consisting of X and Y values. By default, the origin of the coordinate system for each control is the upper left corner, which has coordinates of (0,0), and the coordinates are measured in screen pixels. The System.Drawing namespace contains a variety of structures that can be used to describe locations or regions within this coordinate system. These structures are summarized in Table 7.2.

Table 7.2 Coordinate and Shape Structures

Structure

Description

Point Represents a single point with Integer (Int) values of X and Y.
PointF Represents a single point with Single (float) values of X and Y.
Size Represents a rectangular size consisting of paired Height and Width values, as Integers.
SizeF A rectangular size consisting of a pair of Single (float) values, representing Height and Width.
Rectangle A representation of a rectangular region of the drawing surface with Top, Bottom, Left, and Right edges specified by Integer (Int) values.
RectangleF A representation of a rectangular region of the drawing surface with Top, Bottom, Left, and Right edges specified by Single (float) values.

It should be obvious from Table 7.2 that there are two flavors of structure. There are those that take Integer values and those that take floating-point values. Integer value types, such as Point, Size, and Rectangle, can be implicitly converted to their floating-point counterparts. To convert a floating-point type to an integral type, however, you must explicitly convert each coordinate from the floating-point type to the integral type. For example:

Visual Basic .NET

Dim myPoint As Point
Dim myPointF As New PointF(13.5,33.21)
myPoint = New Point(CInt(myPointF.X), CInt(myPointF.Y))

Visual C#

Point myPoint;
PointF myPointF = new PointF(13.5F,33.21F);
myPoint = new Point((int)myPointF.X, (int)myPointF.Y);

It is important to note the relationship between the Size structures and the Rectangle structures. Although both denote a rectangular region of the drawing surface, the Size structures only indicate a size of a rectangle and do not specify position, whereas the Rectangle structures indicate the actual position of a specific rectangle on the drawing surface. You can create a Rectangle by supplying a Size and a Point that serves as the upper left corner of the Rectangle on the drawing surface. For example:

Visual Basic .NET

Dim myOrigin As New Point(10, 10)
Dim mySize As New Size(20, 20)
' Creates a 20 by 20 rectangle with point(10,10) as the upper
' left corner
Dim myRectangle As New Rectangle(myOrigin, mySize)

Visual C#

Point myOrigin = new Point(10, 10);
Size mySize = new Size(20, 20);
// Creates a 20 by 20 rectangle with point(10,10) as the upper
// left corner
Rectangle myRectangle = new Rectangle(myOrigin, mySize);

Drawing Shapes

The Graphics object encapsulates a wide variety of methods that can be used to render simple and complex shapes to the screen. There are two general varieties of these methods. Those that begin with “Draw” are used to draw line structures, such as lines, arcs, and outlines of shapes. Those that begin with “Fill” are used to render solid shapes, such as filled rectangles, ellipses, or polygons. These methods are summarized in Tables 7.3 and 7.4.

Table 7.3 Methods for Drawing Line Structures

Method

Description

DrawArc Draws an arc representing a portion of an ellipse.
DrawBezier Draws a Bezier spline.
DrawBeziers Draws a series of Bezier splines.
DrawClosedCurve Draws a closed curve through a series of points.
DrawCurve Draws an open curve through a series of points.
DrawEllipse Draws an ellipse defined by a bounding rectangle.
DrawLine Draws a line connecting two points.
DrawLines Draws a series of lines connecting an array of points.
DrawPath Draws a specified GraphicsPath object representing a complex shape.
DrawPie Draws a pie shape representing a slice of an ellipse.
DrawPolygon Draws a polygon created from a specified series of points.
DrawRectangle Draws a rectangle.
DrawRectangles Draws a series of rectangles.

Table 7.4 Methods for Rendering Filled Shapes

Method

Description

FillClosedCurve Renders a filled closed curve specified by an array of points.
FillEllipse Renders a filled ellipse.
FillPath Renders a filled GraphicsPath object representing a complex shape.
FillPie Renders a filled pie shape representing a slice of an ellipse.
FillPolygon Renders a filled polygon specified by an array of points.
FillRectangle Renders a filled rectangle.
FillRectangles Renders a series of filled rectangles.
FillRegion Renders a filled Region object that usually corresponds to a complex shape.

Each of these methods takes a different set of parameters that specify the coordinate points and location of the shapes to be drawn. Each method also requires an object to actually perform the rendering. For line structures, this object is a Pen. For filled shapes, the required object is a Brush.

Colors, Brushes, and Pens

Colors, Brushes, and Pens are used to determine how a graphical image is rendered. Brushes are used to render filled shapes, Pens are used to render lines and arcs, and Colors are used to specify the color to display.

Colors

The Color structure is found in the System.Drawing namespace and represents a single color. Each individual color is derived from four values: Alpha, which represents transparency, and Red, Green, and Blue values. Each of these parameters can have a range from 0 to 255. You can create a new color by specifying these values with the Color.FromArgb method, like this:

Visual Basic .NET

Dim myColor As Color
myColor = Color.FromArgb(128, 255, 12, 43)

Visual C#

Color myColor;
myColor = Color.FromArgb(128, 255, 12, 43);

If you are working solely with opaque colors, you can omit the Alpha parameter and specify only Red, Green, and Blue values, as follows:

Visual Basic .NET

Dim myColor As Color
myColor = Color.FromArgb(255, 12, 43)

Visual C#

Color myColor;
myColor = Color.FromArgb(255, 12, 43);

You can also specify one of the named colors in the .NET Framework. These colors are provided to allow easy reference to a wide array of known colors. The following demonstrates an example:

Visual Basic .NET

Dim myColor As Color
myColor = Color.Tomato 

Visual C#

Color myColor;
myColor = Color.Tomato;

Brushes

Brushes are used to render filled shapes. All brushes derive from the abstract base class Brush and provide different implementations of objects that are used to render filled objects in different styles. The different types of brushes and the namespaces they can be found in are summarized in Table 7.5.

Table 7.5 Types of Brushes

Type

Namespace

Description

SolidBrush System.Drawing A brush that uses a single, solid color.
TextureBrush System.Drawing A brush that fills closed objects with an image.
HatchBrush System.Drawing.Drawing2D A brush that paints using a hatched pattern.
LinearGradientBrush System.Drawing.Drawing2D A brush that blends two colors along a gradient.
PathGradientBrush System.Drawing.Drawing2D A brush that can be used to render complex gradient effects.

Creating a SolidBrush is as simple as specifying the color. For example:

Visual Basic .NET

Dim myBrush As New SolidBrush(Color.PapayaWhip)

Visual C#

SolidBrush myBrush = new SolidBrush(Color.PapayaWhip);

Other kinds of brushes have more complex constructors and require additional parameters. For example, a TextureBrush requires an Image object, and a LinearGradientBrush requires two colors and a variety of other parameters, depending on which constructor is used.

Pens

Pens are used to draw lines and arcs, and can be used to apply a variety of special effects to line structures. There is only one Pen class, and it cannot be inherited. Creating a pen can be as easy as specifying the color, for example:

Visual Basic .NET

Dim myPen As New Pen(Color.BlanchedAlmond)

Visual C#

Pen myPen = new Pen(Color.BlanchedAlmond);

This creates a pen with a default width of one. You can also specify the width in the constructor, like this:

Visual Basic .NET

Dim myPen As New Pen(Color.Lime, 4)

Visual C#

Pen myPen = new Pen(Color.Lime, 4);

The previous example creates a pen with a width of four. You can also create a pen from an already existing brush. This allows you to create a pen that matches whatever visual scheme you are using in your interface and is particularly useful if you are using complex shading or other effects. The following example demonstrates how to create a pen from a preexisting brush called myBrush:

Visual Basic .NET

Dim myPen As New Pen(myBrush)

Visual C#

Pen myPen = new Pen(myBrush);

You can specify a width when creating a pen from a brush as well.

System Colors, Brushes, and Pens

When designing the user interface of your application, you might want any custom UI components to have the same look and feel as the system. The .NET Framework exposes the colors used by the system through the SystemColors class. This class contains a set of static members that expose the colors currently being used by the system. Thus, you can design your custom UI elements with system colors, and at run time, they will be rendered using the current system palette. The following shows how to access one of the system colors:

Visual Basic .NET

Dim myColor as Color = SystemColors.HighlightText

Visual C#

Color myColor = SystemColors.HighlightText;

In addition to the SystemColors class, the .NET Framework also provides a SystemPens class and a SystemBrushes class that provide access to default pens and brushes. You can use the members of these classes just as you would use any other Pen or SolidBrush.

Rendering Simple Shapes

You can use the methods provided by the Graphics class to draw a variety of simple shapes. The methods that can be used for these procedures were summarized in Table 7.3.

All of the methods that render line structures require a valid Pen object. Likewise, methods for rendering filled shapes require a valid Brush object. You must also supply whatever other objects the appropriate method requires. For example, the following demonstrates how to render an outlined rectangle with the DrawRectangle method:

Visual Basic .NET

' Creates the Rectangle object
Dim myRectangle As New Rectangle(0, 0, 30, 20)
' Creates the Graphics object that corresponds to the form
Dim g As Graphics = Me.CreateGraphics()
' Uses a system pen to draw the rectangle)
g.DrawRectangle(SystemPens.ControlDark, myRectangle)
' Disposes the Graphics object
g.Dispose()

Visual C#

// Creates the Rectangle object
Rectangle myRectangle = new Rectangle(0, 0, 30, 20);
// Creates the Graphics object that corresponds to the form
Graphics g = this.CreateGraphics();
// Uses a system pen to draw the rectangle)
g.DrawRectangle(SystemPens.ControlDark, myRectangle);
// Disposes the Graphics object
g.Dispose();

You should always call Dispose on your Graphics objects when you are finished with them. Because they hold a lot of system resources, failure to call Dispose can cause application performance to degrade. You should also call Dispose on any Pen or Brush objects that you have created. The following example demonstrates how to render a filled ellipse and properly dispose of the Brush and Graphics objects:

Visual Basic .NET

Dim myBrush As New SolidBrush(Color.MintCream)
Dim g As Graphics = Me.CreateGraphics()
' The ellipse will be inscribed within the rectangle
Dim myRectangle As New Rectangle(0, 0, 30, 20)
g.FillEllipse(myBrush, myRectangle)
' Dispose the Graphics object and the Brush
g.Dispose()
myBrush.Dispose()

Visual C#

SolidBrush myBrush = new SolidBrush(Color.MintCream);
Graphics g = this.CreateGraphics();
// The ellipse will be inscribed within the rectangle
Rectangle myRectangle = new Rectangle(0, 0, 30, 20);
g.FillEllipse(myBrush, myRectangle);
// Dispose the Graphics object and the Brush
g.Dispose();
myBrush.Dispose();

To render a simple shape

  1. Create a Graphics object that represents the drawing surface you want to render upon.
  1. Create any additional objects you might need. These can include any objects that specify the coordinates and shapes you are rendering, such as Point or Rectangle objects, or any Pens (for line structures) or Brushes (for filled shapes) you might need.
  1. Call the appropriate method of the Graphics object.
  1. Dispose any Pen or Brush objects that you have created.
  1. Dispose the Graphics object.

Rendering Text

You can use the Graphics object to render text by using the DrawString method, which renders a String of text on the screen. The text is displayed in a specified font and is rendered using a specified Brush object. The method also requires some kind of location in the coordinate system of the drawing surface, such as a PointF to designate the upper left corner. The following example demonstrates how to render a String with the Graphics.DrawString method:

Visual Basic .NET

' This example uses the SystemBrush class to supply one of the
' system brushes
Dim g As Graphics = me.CreateGraphics()
Dim myString As String = "Hello World"
Dim myFont As New Font("Times New Roman", 36, FontStyle.Regular)
' The final two parameters are X and Y coordinates
g.DrawString(myString, myFont, SystemBrushes.Highlight, 0, 0)
' Always dispose your Graphics object
g.Dispose()

Visual C#

// This example uses the SystemBrush class to supply one of the
// system brushes
Graphics g = this.CreateGraphics();
String myString = "Hello World";
Font myFont = new Font("Times New Roman", 36, FontStyle.Regular);
// The final two parameters are X and Y coordinates
g.DrawString(myString, myFont, SystemBrushes.Highlight, 0, 0);
// Always dispose your Graphics object
g.Dispose();

To render text

  1. If necessary, create Font and Brush objects appropriate to the style in which you want to render the String.
  1. Obtain a reference to the Graphics object associated with the drawing surface you want to render the text on.
  1. Call the Graphics.DrawString method, specifying the appropriate String, Font, Brush, and location.
  1. Dispose the Graphics object by calling Graphics.Dispose.

Rendering Complex Shapes

At times you will find it necessary to render complex shapes. Although simple shapes such as rectangles, ellipses, and polygons have built-in methods to facilitate their rendering, complex shapes require a little more planning. The primary object used for rendering complex shapes is the GraphicsPath object. The GraphicsPath object is a member of the System.Drawing.Drawing2D namespace and can describe any kind of closed shape or set of shapes. Thus, you could have a GraphicsPath that consisted of ellipses, rectangles, and other regular objects, combined with irregular or amorphous shapes.

Creating a GraphicsPath

You create a GraphicsPath object by making a call to one of the GraphicsPath constructors. The simplest GraphicsPath constructor takes no parameters.

Visual Basic .NET

Dim myPath As New Drawing2D.GraphicsPath()

Visual C#

GraphicsPath myPath = new Drawing2D.GraphicsPath();

Additionally, you can specify arrays of points and bytes that describe the GraphicsPath. The array of points provides coordinates to map the path to, and the array of bytes describes what kind of line passes through the points. To create more readable and maintainable code, you can convert the array of bytes from the System.Drawing.Drawing2D.PathPointType enum. The following creates a very simple GraphicsPath:

Visual Basic .NET

' This example assumes Imports System.Drawing.Drawing2D
Dim myPath As New GraphicsPath(New Point() {New Point(1, 1), _
   New Point(32, 54), New Point(33, 5)}, New Byte() _
   {CType(PathPointType.Start, Byte), CType(PathPointType.Line, _
   Byte), CType(PathPointType.Bezier, Byte)})

Visual C#

// This example assumes using System.Drawing.Drawing2D
GraphicsPath myPath = new GraphicsPath(new Point[] {new Point(1, 1),
   new Point(32, 54), new Point(33, 5)}, new byte[] {
   (byte)PathPointType.Start, (byte)PathPointType.Line,
   (byte)PathPointType.Bezier});

Once created, you can add figures to the GraphicsPath. A figure represents a closed shape including simple shapes, such as ellipses and rectangles, and more complex shapes, such as irregular curves and font characters.

The GraphicsPath class contains several methods that allow you to add figures to the path. These are summarized in Table 7.6.

Table 7.6 Methods for Adding Figures

Method

Description

AddClosedCurve Adds a closed curve described by an array of points to the GraphicsPath.
AddEllipse Adds an ellipse to the GraphicsPath.
AddPath Adds a specified instance of GraphicsPath to the current path.
AddPie Adds a pie shape to the GraphicsPath.
AddPolygon Adds a polygon described by an array of points to the GraphicsPath.
AddRectangle Adds a rectangle to the GraphicsPath.
AddRectangles Adds an array of rectangles to the GraphicsPath .
AddString Adds a graphical representation of a string to the GraphicsPath, in the specified font.

In addition to directly adding figures to the GraphicsPath, you can create figures by adding lines, arcs, and curves. You can begin a figure by calling GraphicsPath.StartFigure. After calling this method, you can use methods supplied by the GraphicsPath class to add line elements to the figure. Once you have completed your figure, you can call GraphicsPath.CloseFigure to close the figure. This automatically connects the last point created with the first one in the figure. The following example demonstrates:

Visual Basic .NET

Dim myPath As New Drawing2D.GraphicsPath()
myPath.StartFigure()
' Insert code to add line elements to the figure here
myPath.CloseFigure()

Visual C#

GraphicsPath myPath = new Drawing2D.GraphicsPath();
myPath.StartFigure();
// Insert code to add line elements to the figure here
myPath.CloseFigure();

If you call StartFigure and then call StartFigure again without calling CloseFigure, the first figure is left open.

NOTE



At run time, any open figures are automatically closed when rendered by adding a line between the first point in the figure and the last.

Table 7.7 summarizes the GraphicsPath methods that can be used to add line elements to a figure.

Table 7.7 Methods for Adding Line Elements

Method

Description

AddArc Adds an arc to the current figure.
AddBezier Adds a Bezier curve to the current figure.
AddBeziers Adds a series of connected Bezier curves to the current figure.
AddCurve Adds a curve described by an array of points to the current figure.
AddLine Adds a line to the current figure.
AddLines Adds a series of connected lines to the current figure.

To render a complex shape

  1. Obtain a reference to the Graphics object associated with the drawing surface you want to render on.
  1. Create a new instance of the GraphicsPath class.
  1. Add figures to the GraphicsPath using the methods the GraphicsPath class provides.
  1. Call Graphics.DrawPath to draw the outline of the path or Graphics.FillPath to draw a filled GraphicsPath.
  1. Dispose the Graphics object.

Lesson Summary

  • The Graphics object is the principal object used in rendering graphics. It represents a drawing surface and provides methods for rendering to that drawing surface.
  • Pens, Brushes, and Colors are objects that are used to control how a graphic is rendered to a drawing surface. Pens draw lines, Brushes fill shapes, and Colors represent colors. You can use SystemPens, SystemBrushes, and SystemColors to maintain a coherent appearance with the rest of the application.
  • Simple shapes and text can be rendered using the methods provided by the Graphics object.
  • Complex shapes should be defined in terms of a GraphicsPath object, which exposes a variety of methods to facilitate defining complex shapes. When complete, a GraphicsPath object can be rendered by the Graphics object.

 

Posted in Graphic Application using with .Net | Leave a Comment »