Link to home
Start Free TrialLog in
Avatar of FSA7
FSA7

asked on

draw program with struct and arrays

Hi I need to create a drawing program, where user chooses to draw a line, or a box or freehand.

I was advised that best way, would be to create a struct of 2 points, or 4 coordiantes, and then an array of these structs and then with mouse up and down event to store coordinates in the array.

Can anyone please give a start in how to create  an array of the struct and add lines into array ? and also how would paint() method run through the entire array drawing all the lines.

Public Structure coordinates
        Public x1, y1, x2, y2 As Double
    End Structure

    Dim coor As coordinates

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim myArray() As Double


    End Sub

    Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
        If chkline.Checked Then
            coor.x1 = e.X
            coor.y1 = e.Y
        End If

    End Sub

    Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseUp
        If chkline.Checked Then
            coor.x2 = e.X
            coor.y2 = e.Y
            Call Refresh()
        End If
    End Sub
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

My suggestion would be to use a GraphicsPath from the GDI+ library.  It can store and draw lines, curves, and rectangles.  The GraphicsPath class has methods, such as AddLine, AddRectangle, AddCurve, AddBezier, AddEllipse, AddPolygon, etc. to make it easier to add the appropriate object to the path.  

Here is an example of using a GraphicsPath for drawing:
http://www.codeproject.com/vb/net/Freehand_Drawing.asp

Bob
Take a look at the solution I posted in this PAQ:
https://www.experts-exchange.com/questions/21035266/How-can-I-create-a-component-that-draws-a-line-in-runtime.html

I have developed more complex applications using this approach that can handle boxes, polygons, curves, etc.

~IM
Avatar of Gipsy
Gipsy

Hi thanks for your replys, sorry for answering late, as i was away. I know that there are a lot of ways to create the drawing programs. And i would like to try the approach of arrays and structs, i just dont know how to make sure  how  to create an array of the structs that store the points and pen details.

Can you please give me a tip,
Thanks
FSA7, sorry for asking questions in your post, i am reading the chapter about the drawing in vb.net and am also interested in the answer to your question and unfortunately dont have an answer myself.

Avatar of FSA7

ASKER

That's ok, as i agree with your point that there  hundreds of ways to make a program to do the same thing. Again, i have a condition for using structs and arrays to create to draw lines, boxes and freehand.

Thanks IdleMind and The LearnedOne,The LearnedOne i've seen the solution you sent before posting this question.
The free hand code, works well, but in my program, I d like to  make sure that line and boxes are all remembered  on the panel and not cleared when i will choose to draw smth else. And i believe from what i read that using array, and tracking the lines in the array is a good thing to do, i just need some help, as i never used structs and arrays together before in VB.net.

So, above i posted an example of the structure and wrote  simple mouse up and down events just for drawing the lines, there is an array created as well but not yet tied to the overall picture. Can you please just help me with getting the program to draw lines and get them all displayed on one panel, rather than lines rewriting each other, everytime a new is drawn.

Thanks for your help!


Are you working with persistent graphics (using the Graphics object from the Paint event) or non-persistent graphics (CreateGraphics method)?

Bob
Avatar of FSA7

ASKER

I am working with the Paint event
This is the bare bones code needed to draw lines on a panel:

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents chkline As System.Windows.Forms.CheckBox
    Friend WithEvents Panel1 As System.Windows.Forms.Panel
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.chkline = New System.Windows.Forms.CheckBox
        Me.Panel1 = New System.Windows.Forms.Panel
        Me.SuspendLayout()
        '
        'chkline
        '
        Me.chkline.Checked = True
        Me.chkline.CheckState = System.Windows.Forms.CheckState.Checked
        Me.chkline.Location = New System.Drawing.Point(8, 8)
        Me.chkline.Name = "chkline"
        Me.chkline.Size = New System.Drawing.Size(104, 16)
        Me.chkline.TabIndex = 0
        Me.chkline.Text = "Line"
        '
        'Panel1
        '
        Me.Panel1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.Panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
        Me.Panel1.Location = New System.Drawing.Point(8, 32)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(280, 224)
        Me.Panel1.TabIndex = 1
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.Panel1)
        Me.Controls.Add(Me.chkline)
        Me.Name = "Form1"
        Me.Text = "Line Example"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Structure coordinates
        Public x1, y1, x2, y2 As Single
    End Structure

    Private coords() As coordinates
    Private down As Boolean = False
    Private prevClip As Rectangle

    Private Sub Panel1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseDown
        If chkline.Checked And e.Button = MouseButtons.Left Then
            ' build a new set of coordinates
            Dim newIndex As Integer
            If Not (coords Is Nothing) Then
                newIndex = coords.GetLength(0)
            Else
                newIndex = 0
            End If
            ReDim Preserve coords(newIndex)
            coords(newIndex).x1 = e.X
            coords(newIndex).y1 = e.Y
            coords(newIndex).x2 = e.X
            coords(newIndex).y2 = e.Y

            ' draw the new line on the panel
            ' at this juncture, it is just a point
            Dim sp As Point = Panel1.PointToScreen(New Point(e.X, e.X))
            ControlPaint.DrawReversibleLine(sp, sp, Panel1.BackColor)

            ' set our mousedown flag
            down = True

            ' clip the cursor so it stays within the panel
            prevClip = Cursor.Clip
            Cursor.Clip = Me.RectangleToScreen(New Rectangle(Panel1.Location, Panel1.Size))
        End If
    End Sub

    Private Sub Panel1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseMove
        If down Then
            ' erase the old line
            Dim newIndex As Integer = coords.GetUpperBound(0)
            Dim sp1 As Point = Panel1.PointToScreen(New Point(coords(newIndex).x1, coords(newIndex).y1))
            Dim sp2 As Point = Panel1.PointToScreen(New Point(coords(newIndex).x2, coords(newIndex).y2))
            ControlPaint.DrawReversibleLine(sp1, sp2, Panel1.BackColor)

            ' update the coordinates for our new point
            ' and draw the new line
            coords(newIndex).x2 = e.X
            coords(newIndex).y2 = e.Y
            sp2 = Panel1.PointToScreen(New Point(coords(newIndex).x2, coords(newIndex).y2))
            ControlPaint.DrawReversibleLine(sp1, sp2, Panel1.BackColor)
        End If
    End Sub

    Private Sub Panel1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseUp
        If down Then
            ' finalize the coordinates of our new line
            down = False
            Dim newIndex As Integer = coords.GetUpperBound(0)
            coords(newIndex).x2 = e.X
            coords(newIndex).y2 = e.Y
            ' reset cursor clipping
            Cursor.Clip = prevClip
            Panel1.Refresh()
        End If
    End Sub

    Private Sub Panel1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
        ' draw each line in the array
        If Not (coords Is Nothing) Then
            Dim g As Graphics = e.Graphics
            Dim coord As coordinates

            For Each coord In coords
                g.DrawLine(Pens.Black, coord.x1, coord.y1, coord.x2, coord.y2)
            Next
        End If
    End Sub

End Class
Avatar of FSA7

ASKER

Hi,

Thanks for the code Idle_Mind.  I am not going to use the exact code of yours, in fact i got another idea, and you code helped me with that.

Can you see how this looks and if you think this may work. The counter is the number of items in the array. In mouse-down event, I store the first endpoint in x1, y1 of the current position in the array; in mouse-ups I store the second endpoint in x2, y2 of the current position, then increment the current position, and call
refresh.,

I try running it, but it doesnt draw the line, why ?

Public Structure coordinates
        Public x1, y1, x2, y2 As Integer

    End Structure
    Dim coords() As coordinates
    Dim counter As Integer = 0


    Private Sub panel1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
        If chkline.Checked And e.Button = MouseButtons.Left Then
            coords(counter).x1 = e.X
            coords(counter).y1 = e.Y

        End If
    End Sub

    Private Sub panel1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseUp

        coords(counter).x2 = e.X
        coords(counter).y2 = e.Y
        counter = counter + 1
        Panel1.Refresh()

    End Sub


    Private Sub Panel1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
        ' draw each line in the array
        If Not (coords Is Nothing) Then
            Dim g As Graphics = e.Graphics
            Dim coord As coordinates
            For Each coord In coords
                g.DrawLine(Pens.Black, coord.x1, coord.y1, coord.x2, coord.y2)
            Next
        End If
    End Sub
Three things wrong:

1) The panel1_MouseDown and panel1_MouseUp events were handling MyBase.MouseDown instead of Panel.MouseDown
2) You never initialized your coords array
3) You never resize your coords array with Redim Preserve.

Here is how it should look:

    Private Sub panel1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseDown
        If chkLine.Checked And e.Button = MouseButtons.Left Then
            ReDim Preserve coords(counter)
            coords(counter).x1 = e.X
            coords(counter).y1 = e.Y
        End If
    End Sub

    Private Sub panel1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseUp
        coords(counter).x2 = e.X
        coords(counter).y2 = e.Y
        counter = counter + 1
        Panel1.Refresh()
    End Sub

Note that if you want the line to be drawn in realtime as you drag the mouse, you must use the ControlPaint.DrawReversibleLine() function as in my example.

Also, you don't need the counter variable.  Take a close look at my example to learn how to use the GetLength() and GetUpperBound() functions to determine the size and upper limit of an array.

If an array is not actually required, it would make much more sense to use a Class to store your coordinates and then store the instances of your class in an ArrayList.

~IM
Avatar of FSA7

ASKER

Wow, that worked !!! Thank you sooo much ! Plus i've learned new things !
If i add 20 more points can  ask one last qestion please !
I am drawing a rectangle with the use of the array in place, but  in the mouse up, instead of adding one line to the array, i want to add four (like if the mousedown point is x1, y1 and the mouseup point is x2,
y2, i am adding  lines x1, y1 to x2, y1, then x2, y1 to x2, y2, etc.)

So below i have added like described above, but with what i have, it only draws one vertical line. Do you know what i do wrong ?

Private Sub panel1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseUp
        If chkline.Checked And e.Button = MouseButtons.Left Then
            coords(counter).x2 = e.X
            coords(counter).y2 = e.Y
            counter = counter + 1
            Panel1.Refresh()
        End If
        If chkbox.Checked And e.Button = MouseButtons.Left Then
            coords(counter).x2 = e.X
            coords(counter).y2 = coords(counter).y1
            coords(counter).x2 = e.X
            coords(counter).y2 = e.Y
            coords(counter).x2 = coords(counter).x1
            coords(counter).y2 = e.Y
            counter = counter + 1
            Panel1.Refresh()
        End If
Avatar of FSA7

ASKER

Maybe the problem i am experiencing is because of the counter ?
I also tried my freehand code:

in mouse down  x1, y1 are set.  In mouse move  a line from
x1, y1 is added to the current point, then  x1, y1 are set to the current point so that
next line will go from there.

  Private Sub Panel1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseMove
        If chkfreehand.Checked And e.Button = MouseButtons.Left Then
            coords(counter).x2 = e.X
            coords(counter).y2 = e.Y
            coords(counter).x1 = coords(counter).x2
            coords(counter).y1 = coords(counter).y2
            Panel1.Refresh()
        End If
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of FSA7

ASKER

Thanks for your help, Idle Mind!