Search code examples
.netvb.netgraphicsdrawing

VB.NET drawing multiple lines on a PictureBox without removing previous changes


I'm trying to draw multiple lines on a VB.NET PictureBox without deleting the previous changes. If I try to draw two lines on a PictureBox during different times with e.graphics, then it would delete previous lines/changes.

I'm using this code on a PictureBox's paint event.

Public Class DrawLine

Dim point1X As Integer = 10
Dim point1Y As Integer = 10

Dim point2X As Integer = 50
Dim point2Y As Integer = 50

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    Dim pt As New Point(point1X, point1Y)
    Dim pt1 As New Point(point2X, point2Y)
    e.Graphics.DrawLine(Pens.Green, pt, pt1)    
End Sub

End Class

This example perfectly draws a green line inside the PictureBox.

However, when I have a button that draws another line on the PictureBox after the form has already been painted, the previous line drawn disappears.

For example, if I have a function like this that draws another line

Private Function DrawAnotherLine(xPos1 As Integer, yPos1 As Integer, xPos2 As Integer, yPos2 As Integer)

point1X = xPos1
point1Y = yPos1

point2X = xPos2
point2Y = yPos2

PictureBox1.Invalidate()
End Function

It deletes the first line drawn and only draws the second line. I want it draw the second line on top of the first line and not just delete the first line though, how would I do this?

(Note: I cannot include drawing the first line in the function, as this function will be used multiple times on top of the lines its already drawn).


Solution

  • GDI+ drawing is pretty simple:

    1. Declare one or more fields to store the data that represents your drawing.
    2. Handle the Paint event of the control you want to draw on.
    3. In the event handler in step 2, get the data from the field(s) declared in step 1 and perform the drawing.
    4. To change the drawing, modify the field(s) declared in step 1 and call Invalidate on the control.

    If you want to be able to draw multiple lines then you first need a data structure that represents a single line and then somewhere to store multiple instances of that data structure. The most obvious way to do that is to define a class or structure that contains the data for a single line, e.g.

    Public Class Line
    
        Public ReadOnly Property StartPoint As Point
    
        Public ReadOnly Property EndPoint As Point
    
        Public Sub New(startPoint As Point, endPoint As Point)
            Me.StartPoint = startPoint
            Me.EndPoint = endPoint
        End Sub
    
    End Class
    

    and to then declare a field that refers to a collection of that type:

    Private lines As New List(Of Line)
    

    Your Paint event handler then draws all the lines in the collection:

    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
        For Each line In lines
            e.Graphics.DrawLine(Pens.Black, line.StartPoint, line.EndPoint)
        Next
    End Sub
    

    To draw a new line, you add a new Line object to the collection and Invalidate the control:

    Private Sub AddNewLine(startPoint As Point, endPoint As Point)
        lines.Add(New Line(startPoint, endPoint))
        PictureBox1.Invalidate()
    End Sub
    

    How you get those points is up to you. You might record the location on a MouseDown event and then call AddNewLine on a MouseUp event or you might do something else entirely.

    It is also preferable to specify the smallest area possible when calling Invalidate, but that's beyond the scope of this question.