Painting

In this part of the Visual Basic Winforms tutorial, we will do some painting. Painting is used, when we want to change or enhance an existing control. Or if we are creating a custom control from scratch. To do the painting, we use the painting API provided by the Winforms library. The painting is done within a method, that we plug into the Paint event.

The System.Drawing namespace provides access to GDI+ basic graphics functionality. More advanced functionality is provided in the System.Drawing.Drawing2D, System.Drawing.Imaging, and System.Drawing.Text namespaces. The Graphics class provides methods for drawing on the form.

Hatches

The HatchBrush object is used to fill the interiors of the shapes. There are several built-in patterns that we can use.

' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws nine rectangles.
' The interiors are filled with
' different built-in patterns.
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com


Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D


Public Class WinVBApp
    Inherits Form


    Public Sub New

       Me.Text = "Hatches"
       Me.Size = New Size(360, 300)
       
       AddHandler Me.Paint AddressOf Me.OnPaint
       
       Me.CenterToScreen

    End Sub
        
        
    Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
    
        Dim g As Graphics = e.Graphics
            
        Dim hb As HatchBrush = New HatchBrush(HatchStyle.Cross, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 10, 15, 90, 60)

        hb = New HatchBrush(HatchStyle.Percent05, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 130, 15, 90, 60)

        hb = New HatchBrush(HatchStyle.SolidDiamond, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 250, 15, 90, 60)

        hb = New HatchBrush(HatchStyle.DiagonalBrick, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 10, 105, 90, 60)

        hb = New HatchBrush(HatchStyle.Divot, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 130, 105, 90, 60)

        hb = New HatchBrush(HatchStyle.Wave, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 250, 105, 90, 60)

        hb = New HatchBrush(HatchStyle.ZigZag, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 10, 195, 90, 60)

        hb = New HatchBrush(HatchStyle.Sphere, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 130, 195, 90, 60)

        hb = New HatchBrush(HatchStyle.Shingle, Color.Black, Me.BackColor)
        g.FillRectangle(hb, 250, 195, 90, 60)

        g.Dispose
        hb.Dispose

    End Sub


    Public Shared Sub Main
        Application.Run(New WinVBApp)
    End Sub
   
End Class

This time we fill nine rectangles with nine different patterns, called hatches.

Dim hb As HatchBrush = New HatchBrush(HatchStyle.Cross, Color.Black, Me.BackColor)

Here we create a HatchBrush object. The parameters are the hatch style and the foreground and the background colors. The background color is set to the color of the form, so that it looks like we have drawn onto the form.

g.FillRectangle(hb, 10, 15, 90, 60)

We fill the rectangle with the specified hatch brush.

Hatches
Figure: Hatches

Basic shapes

The following example draws some basic shapes on the form control.

' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws basic shapes available
' in Winforms
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com


Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic


Public Class WinVBApp
    Inherits Form


    Public Sub New

       Me.Text = "Basic Shapes"
       Me.Size = New Size(420, 280)
       
       AddHandler Me.Paint AddressOf Me.OnPaint
       
       Me.CenterToScreen

    End Sub
        
        
    Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
    
        Dim g As Graphics = e.Graphics
        g.SmoothingMode = SmoothingMode.AntiAlias
        
        g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)
        g.FillRectangle(Brushes.Gray, 180, 20, 80, 80)
        
        g.FillEllipse(Brushes.Gray, 30, 120, 100, 100)
        g.FillEllipse(Brushes.Gray, 160, 130, 100, 70)
        
        Dim points(5) As Point
        
        points(0) = New Point(300, 40)
        points(1) = New Point(340, 15)
        points(2) = New Point(380, 40)
        points(3) = New Point(380, 80)
        points(4) = New Point(340, 105)
        points(5) = New Point(300, 80)
 
        g.FillPolygon(Brushes.Gray, points)
        g.FillPie(Brushes.Gray, New Rectangle(290, 130, 90, 90), 0, 315)

        g.Dispose

    End Sub


    Public Shared Sub Main
        Application.Run(New WinVBApp)
    End Sub
   
End Class

The code example draws six shapes on the form. A rectangle, a square, a circle, an ellipse, a polygon and a pie.

g.SmoothingMode = SmoothingMode.AntiAlias

This makes the drawing smoother.

g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)

This line fills a rectangle with gray color. The parameters are, the brush color, x, y coordinates of the upper-left corner of the rectangle and width and height of the rectangle.

Dim points(5) As Point

points(0) = New Point(300, 40)
points(1) = New Point(340, 15)
...

We create an array of five points.

g.FillPolygon(Brushes.Gray, points)

This line draws a polygon, consisting of six single points.

g.FillPie(Brushes.Gray, New Rectangle(290, 130, 90, 90), 0, 315)

This line draws a pie. The last two parameters are the start angle and sweep angle. In degrees.

Basic shapes
Figure: Basic shapes

Transparent rectangles

Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass.

In computer graphics, we can achieve transparency effects using alpha compositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an alpha channel. (wikipedia.org, answers.com)

' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws ten
' rectangles with different
' levels of transparency
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com


Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic


Public Class WinVBApp
    Inherits Form


    Public Sub New

       Me.Text = "Transparent rectangles"
       Me.Size = New Size(590, 110)
       
       AddHandler Me.Paint AddressOf Me.OnPaint
       
       Me.CenterToScreen

    End Sub
        
        
    Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
    
        Dim g As Graphics = e.Graphics
        
        For i As Integer = 1 to 10
            Dim color As Color = Color.FromArgb(i*25, 0, 0, 255)
            Dim brush As Brush = New SolidBrush(color)
            g.FillRectangle(brush, 50*i, 20, 40, 40)
        Next

        g.Dispose

    End Sub


    Public Shared Sub Main
        Application.Run(New WinVBApp)
    End Sub
   
End Class

In the example we will draw ten rectangles with different levels of transparency.

Dim color As Color = Color.FromArgb(i*25, 0, 0, 255)

This line creates a color object. The first value is the alpha transparency.

Dim brush As Brush = New SolidBrush(color)

We create a brush from the color.

g.FillRectangle(brush, 50*i, 20, 40, 40)

We fill a rectangle with a color.

Transparent rectangles
Figure: Transparent rectangles

Grayscale image

The following example creates a grayscale image.

' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws creates a grayscale 
' clone of a bitmap image
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com


Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D


Public Class WinVBApp
    Inherits Form

    Private rotunda As Bitmap
    Private gs As Bitmap

    Public Sub New

       Me.Text = "Grayscale"
       Me.Size = New Size(290, 150)
       
       rotunda = Me.LoadImage
       gs = GrayScale(rotunda.Clone)
       
       AddHandler Me.Paint AddressOf Me.OnPaint
       
       Me.CenterToScreen

    End Sub
        
    Private Function LoadImage As Bitmap
    
        Try
            rotunda = New Bitmap("rotunda.jpg")
            Return rotunda
        Catch
            Console.WriteLine("Image not found")
            Environment.Exit(1)
        End Try
        
    End Function

    Private Function GrayScale(ByVal image As Bitmap) As Bitmap
        
        Dim w As Integer = image.Width
        Dim h As Integer = image.Height

        For i as Integer = 0 To w-1
            For j As Integer = 0 To h-1
                Dim c As Color = image.GetPixel(i, j)
                Dim lum As Double = 0.299*c.R + 0.587*c.G + 0.114*c.B
                image.SetPixel(i, j, Color.FromArgb(lum, lum, lum))
            Next
        Next
        
        Return image
    
    End Function
        
        
    Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
    
        Dim g As Graphics = e.Graphics
        
        Dim r1 As New Rectangle(15, 15, rotunda.Width, rotunda.Height)
        g.DrawImage(rotunda, r1)

        Dim r2 As New Rectangle(150, 15, gs.Width, gs.Height)
        g.DrawImage(gs, r2)
            
        g.Dispose

    End Sub


    Public Shared Sub Main
        Application.Run(New WinVBApp)
    End Sub
   
End Class

We have two images in our example. A color and a grayscale one.

rotunda = Me.LoadImage

The LoadImage method loads a bitmap from the current working directory of the disk.

gs = GrayScale(rotunda.Clone)

The GrayScale method makes a grayscale image from a color image. We give a copy of the rotunda image as a parameter to this method.

Dim c As Color = image.GetPixel(i, j)

We get a color of a pixel.

Dim lum As Double = 0.299*c.R + 0.587*c.G + 0.114*c.B

This equation calculates a luminocity for a grayscale image. If we scale the the red, green and blue parts of the color with these factors, the human eye sees the image as gray.

image.SetPixel(i, j, Color.FromArgb(lum, lum, lum))

We modify the pixel.

Grayscale image
Figure: Grayscale image

Drawing text

To draw text on the Winforms Form, we use the DrawString method.

' ZetCode Mono Visual Basic Winforms tutorial
'
' This program draws lyrics of
' a song
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com


Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic


Public Class WinVBApp
    Inherits Form


    Public Sub New

       Me.Text = "You know I'm no Good"
       Me.Size = New Size(380, 450)
       
       AddHandler Me.Paint AddressOf Me.OnPaint
       
       Me.CenterToScreen

    End Sub
        
        
    Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
    
        Dim g As Graphics = e.Graphics
        
        Dim ft As New Font("Purisa", 10)
        Dim br As New SolidBrush(Color.Black)

        Dim pt As PointF = New PointF(20.0f, 20.0f)     
        g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)

        pt = New PointF(20.0f, 50.0f)
        g.DrawString("Your rolled up sleeves and your skull t-shirt", ft, br, pt)

        pt = New PointF(20.0f, 80.0f)
        g.DrawString("You say why did you do it with him today?", ft, br, pt)

        pt = New PointF(20.0f, 110.0f)  
        g.DrawString("And sniffed me out like I was tanqueray", ft, br, pt)

        pt = New PointF(20.0f, 160.0f)             
        g.DrawString("Cause you’re my fella, my guy", ft, br, pt)

        pt = New PointF(20.0f, 190.0f)       
        g.DrawString("Hand me your stella and fly", ft, br, pt)

        pt = New PointF(20.0f, 220.0f)             
        g.DrawString("By the time I’m out the door", ft, br, pt)

        pt = New PointF(20.0f, 250.0f)             
        g.DrawString("You tear me down like roger moore", ft, br, pt)

        pt = New PointF(20.0f, 300.0f)             
        g.DrawString("I cheated myself", ft, br, pt)

        pt = New PointF(20.0f, 330.0f)             
        g.DrawString("Like I knew I would", ft, br, pt)

        pt = New PointF(20.0f, 360.0f)
        g.DrawString("I told ya, I was trouble", ft, br, pt)

        pt = New PointF(20.0f, 390.0f)
        g.DrawString("You know that I’m no good", ft, br, pt)

        ft.Dispose
        br.Dispose
        g.Dispose

    End Sub


    Public Shared Sub Main
        Application.Run(New WinVBApp)
    End Sub
   
End Class

In our example, we draw lyrics of a song on the Winforms form.

Dim ft As New Font("Purisa", 10)

We use the Purisa font, of 10 pts height.

Dim pt As PointF = New PointF(20.0f, 20.0f) 

To draw string on the form, we must use floating point values.

g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)

The DrawString method takes the following parameters: text to draw, font, brush and the PointF object.

Lyrics
Figure: Lyrics

Waiting

In this examle, we use transparency effect to create a waiting demo. We will draw 8 lines that will gradually fade out creating an illusion, that a line is moving. Such effects are often used to inform users, that a lengthy task is going on behind the scenes. An example is streaming video over the internet.

' ZetCode Mono Visual Basic Winforms tutorial
'
' This program creates a waiting
' demo
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com


Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections.Generic


Public Class WinVBApp
    Inherits Form

    Dim trs(,) As Integer = New Integer(,) { _
        { 0, 35, 70, 100, 150, 180, 210, 250 }, _
        { 250, 0, 35, 70, 100, 150, 180, 210 }, _
        { 210, 250, 0, 35, 70, 100, 150, 180 }, _
        { 180, 210, 250, 0, 35, 70, 100, 150 }, _
        { 150, 180, 210, 250, 0, 35, 70, 100 }, _
        { 100, 150, 180, 210, 250, 0, 35, 70 }, _
        { 70, 100, 150, 180, 210, 250, 0, 35 }, _
        { 35, 70, 100, 150, 180, 210, 250, 0 } _
    }

    Dim count As Integer = 0
    Dim timer As Timer

    Public Sub New

       Me.Text = "Waiting"
       Me.Size = New Size(250, 150)
       
       timer = New Timer
       timer.Enabled = True
       timer.Interval = 80
       
       AddHandler timer.Tick, AddressOf Me.OnTick
       AddHandler Me.Paint, AddressOf Me.OnPaint
       
       Me.CenterToScreen

    End Sub

    Private Sub OnTick(ByVal sender As Object, ByVal e As EventArgs)
        count = count + 1
        Me.Refresh
    End Sub
        
    Private Sub OnPaint(ByVal sender As Object, ByVal e As PaintEventArgs)
    
        Dim g As Graphics = e.Graphics
        g.SmoothingMode = SmoothingMode.AntiAlias
        
        Dim si As Size = Me.ClientSize
        g.TranslateTransform(si.Width/2, si.Height/2)
        
        For i As Integer = 0 To 7 
            Dim color As Color = Color.FromArgb(trs(count Mod 8, i), 30, 30, 30)
            Dim pen As New Pen(color, 3)
            pen.StartCap = LineCap.Round
            pen.EndCap = LineCap.Round
            g.DrawLine(pen, 0, -10, 0, -40)
            g.RotateTransform(45)
            pen.Dispose
        Next
        
        g.Dispose

    End Sub


    Public Shared Sub Main
        Application.Run(New WinVBApp)
    End Sub
   
End Class

We draw eight lines with eight different alpha values.

timer = New Timer
timer.Enabled = True
timer.Interval = 80
      
AddHandler timer.Tick, AddressOf Me.OnTick

We use Timer to create animation.

Dim trs(,) As Integer = New Integer(,) { _
    { 0, 35, 70, 100, 150, 180, 210, 250 }, _
...
}

This is a two dimensional collection of transparency values used in this demo. There are 8 rows, each for one state. Each of the 8 lines will continuosly use these values.

Dim pen As New Pen(color, 3)
pen.StartCap = LineCap.Round
pen.EndCap = LineCap.Round

We make the lines a bit thicker, so that they are better visible. We draw the lines with rouded caps.

Dim color As Color = Color.FromArgb(trs(count Mod 8, i), 30, 30, 30)

Here we define the transparency value for a line.

g.DrawLine(pen, 0, -10, 0, -40)
g.RotateTransform(45)

We draw 8 lines. They are roteted clockwise.

Waiting
Figure: Waiting

In this chapter, we did some painting in Winforms with Visual Basic.