?
Solved

Drawing line while moving mouse- VB.NET

Posted on 2005-04-24
7
Medium Priority
?
2,789 Views
Last Modified: 2012-06-21
Hi experts. Let's say there is a line drawn in a pictureBox which I want to be able to drag the ends of it. I have a code which works. However, when I resize (stretch or maximize...) the "docked" pictureBox, the line does not stretch and goes out of picture if I shrink the picture. The other problem is that my solution (code) uses lots of memory and reacts slowly.
---An idea is to define the end points in terms of the width/height of pictureBox so it will stretch, but HOW???
Any help will be great.
CODE:==============================
    Private point1_SlopeLine As New Point(250, 300)'Initially somewhere
    Private point2_SlopeLine As New Point(500, 100)

    Private Sub PictureBoxGraph_MouseMove(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.MouseEventArgs) Handles PictureBoxGraph.MouseMove

If eventArgs.X > point2_SlopeLine.X - 50 And eventArgs.X < point2_SlopeLine.X + 50 And eventArgs.Y > point2_SlopeLine.Y - 50 And eventArgs.Y < point2_SlopeLine.Y + 50 Then PictureBoxGraph.Refresh()

point2_SlopeLine.X = eventArgs.X
point2_SlopeLine.Y = eventArgs.Y
 '=========Drawing
Dim linePen As New Pen(Color.Black, 0)
PictureBoxGraph.CreateGraphics.DrawLine(linePen, point1_SlopeLine, Point2_SlopeLine)
                     
End If
End Sub


Private Sub PictureBoxGraph_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBoxGraph.Paint

            Dim linePen As New Pen(Color.Black, 0)
            e.Graphics.DrawLine(linePen, point1_SlopeLine, point2_SlopeLine)
End Sub
0
Comment
Question by:kouroshparsa
  • 4
  • 3
7 Comments
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 500 total points
ID: 13856130
Here is an example app.  Draw lines by clicking and dragging in the PictureBox.  Then resize the form and observe how the lines stay proportional to the size of the box:

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 PictureBox1 As System.Windows.Forms.PictureBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.PictureBox1 = New System.Windows.Forms.PictureBox
        Me.SuspendLayout()
        '
        'PictureBox1
        '
        Me.PictureBox1.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.PictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
        Me.PictureBox1.Location = New System.Drawing.Point(8, 8)
        Me.PictureBox1.Name = "PictureBox1"
        Me.PictureBox1.Size = New System.Drawing.Size(312, 240)
        Me.PictureBox1.TabIndex = 0
        Me.PictureBox1.TabStop = False
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(328, 254)
        Me.Controls.Add(Me.PictureBox1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private lines As New ArrayList
    Private startPoint As Point
    Private endPoint As Point
    Private prevClip As Rectangle

    Private Class Line
        Public ptA As PointF
        Public ptB As PointF
    End Class

    Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        If e.Button = MouseButtons.Left Then
            startPoint = New Point(e.X, e.Y)
            endPoint = startPoint
            ControlPaint.DrawReversibleLine(PictureBox1.PointToScreen(startPoint), PictureBox1.PointToScreen(endPoint), Color.Black)
            prevClip = Cursor.Clip()
            Cursor.Clip = Me.RectangleToScreen(New Rectangle(PictureBox1.Location, PictureBox1.Size))
        End If
    End Sub

    Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        If e.Button = MouseButtons.Left Then
            ControlPaint.DrawReversibleLine(PictureBox1.PointToScreen(startPoint), PictureBox1.PointToScreen(endPoint), Color.Black)
            endPoint = New Point(e.X, e.Y)
            ControlPaint.DrawReversibleLine(PictureBox1.PointToScreen(startPoint), PictureBox1.PointToScreen(endPoint), Color.Black)
        End If
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        If e.Button = MouseButtons.Left Then
            Dim l As New Line
            l.ptA = New PointF(startPoint.X / PictureBox1.Width, startPoint.Y / PictureBox1.Height)
            l.ptB = New PointF(endPoint.X / PictureBox1.Width, endPoint.Y / PictureBox1.Height)
            lines.Add(l)
            Cursor.Clip = prevClip
        End If
    End Sub

    Private Sub PictureBox1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.Resize
        PictureBox1.Refresh()
    End Sub

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        Dim g As Graphics = e.Graphics
        Dim l As Line
        For Each l In lines
            g.DrawLine(Pens.Black, PictureBox1.Width * l.ptA.X, PictureBox1.Height * l.ptA.Y, _
                PictureBox1.Width * l.ptB.X, PictureBox1.Height * l.ptB.Y)
        Next
    End Sub

End Class

0
 
LVL 2

Author Comment

by:kouroshparsa
ID: 13862464
Great. I have some questions about the code:
In
      Private Class Line
           Public ptA As PointF
           Public ptB As PointF
       End Class
Does using another class inside the class have an advantage to using private instances (like private ptA as new PointF ?

What is the idea behind  Cursor.Clip=...           ?

I think the main problem with my code was calling pictureBox.Refresh() instead of ControlPaint.DrawReversibleLine.  to erase the old line. Do you know how it works differently?
Thanks
0
 
LVL 2

Author Comment

by:kouroshparsa
ID: 13862617
I understood that Cursor.Clip keeps the cursor in the frame...
so please consider my other questions.
thanks
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 13863782
In answer to your first question...using a Class (whether it be a private "inner" class or a completely seperate class) is better than using "private instances" as you call them.  The main reason for this is that it makes it easy to store many properties about the "thing" we are trying to represent.  In this case, we are storing lines and the bare minimum then, would be to just store the two endpoints somehow.  It would be easy to remove the class and use locally declared arrays or collections to store the endpoints but then what happens if we want to store more information?  What if we want to add line color, line thickness and line style (solid, dashed, etc.).  Now instead of two local variables, we have five.  Our main code becomes cluttered with information that could instead be elegantly encapsulated in a class.  By moving the information about our thing into a class, we make the main code easier to read and easier to maintain.  All the information about each line is neatly stored inside a single line instance.

With the example I provided, there would be a huge difference bewteen calling Refresh() and DrawReversibleLine().  When the user clicks and drags to create a new line, we could have first created an instance of the Line class and added it to our ArrayList.  Then as the line is moved, we could update the endpoints of that line and call Refresh().  The problem with this approach is that each time Refresh() is called, the PictureBox is erased and EVERY single line is redrawn.  With enough lines on the screen, this can cause noticeable flicker and delay.  By using DrawReversibleLine(), only the new line is erased and moved...none of the other lines need to be refreshed.
0
 
LVL 2

Author Comment

by:kouroshparsa
ID: 13864253
Thanks a lot. Your explanation is very straight forward. Cheers.
0
 
LVL 2

Author Comment

by:kouroshparsa
ID: 13871953
Idle_Mind, do you know if the following are the same?

            selectedPoint = New Point(e.X, e.Y)
'''and
            selectedPoint.x = e.X
            selectedPoint.x = e.Y

I'm wondering because in Java, "New" declares something new in the memory
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 13872959
The following are all equivalent:

        Dim selectedPoint As Point = New Point(e.X, e.Y)
        ' < or >
        Dim selectedPoint As New Point(e.X, e.Y)
        ' < or >
        Dim selectedPoint = New Point(e.X, e.Y)
        ' < or >
        Dim selectedPoint As Point
        selectedPoint.X = e.X
        selectedPoint.Y = e.Y

Just a matter of preference.  In this case, New is not really necessary because Point() is a structure, not a class.
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I think the Typed DataTable and Typed DataSet are very good options when working with data, but I don't like auto-generated code. First, I create an Abstract Class for my DataTables Common Code.  This class Inherits from DataTable. Also, it can …
Introduction When many people think of the WebBrowser (http://msdn.microsoft.com/en-us/library/2te2y1x6%28v=VS.85%29.aspx) control, they immediately think of a control which allows the viewing and navigation of web pages. While this is true, it's a…
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…
Screencast - Getting to Know the Pipeline
Suggested Courses
Course of the Month16 days, 23 hours left to enroll

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question