Solved

VB.NET Problem with pictureBox Paint event

Posted on 2004-08-17
6
7,101 Views
Last Modified: 2012-05-05
Hi experts. I'm trying to upgrade VB6 to VB.NET. As we know, a pictureBox in VB.NET hase no autoRedraw and instead we need to use Paint event.
I could not put my DrawLine code in the paint event since my application draws different curves in different manners (depending on the options of the user)
I've asked this question before at :http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VB_DOT_NET/Q_21070801.html

And Idle-Mind found a good solution using an interface. Now When I try to run the drawing from another window Form, the process fails without an error.(The process works in a single form)
You need the code to understand so here I made a very very simple code:(just copy & paste)

'In the Module:
    Public Interface GraphObject
        Sub Paint(ByRef g As Graphics)
    End Interface

    Public Class LineObject
        Implements GraphObject

        Private pointA As Point
        Private pointB As Point

        Public Sub New(ByVal ptA As Point, ByVal ptB As Point)
            pointA = ptA
            pointB = ptB
        End Sub

        Public Sub Paint(ByRef g As Graphics) Implements GraphObject.Paint
            g.DrawLine(Pens.Black, pointA, pointB)
        End Sub
    End Class

'---------In Form1:
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 Button1 As System.Windows.Forms.Button
    Friend WithEvents PictureBox1 As System.Windows.Forms.PictureBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button
        Me.PictureBox1 = New System.Windows.Forms.PictureBox
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(8, 8)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(80, 24)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Add Line"
        '
        '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, 40)
        Me.PictureBox1.Name = "PictureBox1"
        Me.PictureBox1.Size = New System.Drawing.Size(280, 224)
        Me.PictureBox1.TabIndex = 1
        Me.PictureBox1.TabStop = False
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.PictureBox1)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form1"
        Me.Text = "Peristent Graphics via Classes"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        Dim go As GraphObject
        Dim f2 As New Form2

        For Each go In f2.graphObjects
            go.Paint(e.Graphics)
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim form2 As New Form2
        form2.Show()
    End Sub

End Class
'------In Form2:
Public Class Form2
    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 Button1 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(72, 16)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(72, 48)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Draw"
        '
        'Form2
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(216, 86)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form2"
        Me.Text = "Draw"
        Me.ResumeLayout(False)

    End Sub

#End Region
    Private rnd As New Random(DateTime.Now().Second)
    Public graphObjects As New ArrayList

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click


        Dim x1, y1, x2, y2 As Integer
        x1 = 0
        y1 = 0
        x2 = 100
        y2 = 100

        Dim l As New LineObject(New Point(x1, y1), New Point(x2, y2))
        graphObjects.Add(l)
        Dim Form1 As New Form1
        Form1.PictureBox1.Refresh()
        Me.Hide()
    End Sub
End Class
0
Comment
Question by:kouroshparsa
  • 4
  • 2
6 Comments
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 200 total points
Comment Utility
Try this out:

' --------------------------------------------------------------------------------------
' Form1
' --------------------------------------------------------------------------------------

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 Button1 As System.Windows.Forms.Button
    Friend WithEvents PictureBox1 As System.Windows.Forms.PictureBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button
        Me.PictureBox1 = New System.Windows.Forms.PictureBox
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(8, 8)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(80, 24)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Add Line"
        '
        '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, 40)
        Me.PictureBox1.Name = "PictureBox1"
        Me.PictureBox1.Size = New System.Drawing.Size(280, 224)
        Me.PictureBox1.TabIndex = 1
        Me.PictureBox1.TabStop = False
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 266)
        Me.Controls.Add(Me.PictureBox1)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form1"
        Me.Text = "Peristent Graphics via Classes"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Public graphObjects As New ArrayList
    Private WithEvents f2 As Form2

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        Dim go As GraphObject

        For Each go In graphObjects
            go.Paint(e.Graphics)
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If f2 Is Nothing Then
            f2 = New Form2
            f2.f1 = Me
        End If
        f2.Show()
    End Sub

    Private Sub f2_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles f2.Closed
        f2 = Nothing
    End Sub

    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        If Not (f2 Is Nothing) Then
            f2.Dispose()
            f2 = Nothing
        End If
    End Sub

End Class

' --------------------------------------------------------------------------------------
' Form2
' --------------------------------------------------------------------------------------
Public Class Form2
    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 Button1 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(72, 16)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(72, 48)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Draw"
        '
        'Form2
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(216, 86)
        Me.Controls.Add(Me.Button1)
        Me.Name = "Form2"
        Me.Text = "Draw"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Public f1 As Form1

    Private rnd As New Random(DateTime.Now().Second)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim x1, y1, x2, y2 As Integer
        x1 = rnd.NextDouble() * f1.PictureBox1.Width
        y1 = rnd.NextDouble() * f1.PictureBox1.Height
        x2 = rnd.NextDouble() * f1.PictureBox1.Width
        y2 = rnd.NextDouble() * f1.PictureBox1.Height

        Dim l As New LineObject(New Point(x1, y1), New Point(x2, y2))
        f1.graphObjects.Add(l)
        f1.PictureBox1.Refresh()
        Me.Hide()
    End Sub

End Class
0
 
LVL 2

Author Comment

by:kouroshparsa
Comment Utility
Thanks Idle-Mind. The sample woks very well; however, the real problem is my complicated program.
Now I'm facing another problem without an error and without any graphing action.
I found the problem:
Dim gr As Graphics = FormGraph.DefInstance.pictureBoxGraph.CreateGraphics
Dim XMIN As Double
Dim XMAX As Double
Dim YMIN As Double
Dim YMAX As Double
'--------------------Setting Scale:
XMIN = FormGraph.DefInstance.GraphScaleLeft
XMAX = XMIN + FormGraph.DefInstance.GraphScaleWidth
YMAX = FormGraph.DefInstance.GraphScaleTop
YMIN = YMAX - FormGraph.DefInstance.GraphScaleHeight
SetScale(gr, FormGraph.DefInstance.pictureBoxGraph.Width, FormGraph.DefInstance.pictureBoxGraph.Height, XMIN, XMAX, YMAX, YMIN)
'------------------Graphing:
 Dim l As New LineObject(New Point(x1, y1), New Point(x2, y2))
frmGraph.graphObjects.Add(l)
'=======================


if x1=0 y1=0 and x2=100 y2=100 it does draw a line, but the problem is that I've changed the scale of "gr" which refers to the pictureBox but drawing is done on the object!

Apparently I should change the scale of the pictureBox somewhere else (either in module or in the paint event)
Please help me with that.
By the way, putting "Dim l As New LineObject..." in a loop with 400 iterations is not very good but work!
0
 
LVL 2

Author Comment

by:kouroshparsa
Comment Utility
If you have any improving idea about the following, please let me know:
The main problem with the code is putting "Dim l as New LineObject" in a loop. By the way, since I want to handle big numbers, I can not use "Point(x1,y1)" so I'd change it to Double

Also, about the scale problem, let's clerify:
I put a method named SetScale in the module to change the scale to real graphing scale:
================
Public Sub SetScale(ByVal gr As Graphics, ByVal gr_width As Integer, ByVal gr_height As Integer, ByVal left_x As Single, ByVal right_x As Single, ByVal top_y As Single, ByVal bottom_y As Single)
' Set transformations for the Graphics object
' so its coordinate system matches the one specified.

gr.ResetTransform()

' Scale so the viewport's width and height
' map to the Graphics object's width and height.
gr.ScaleTransform( _
gr_width / (right_x - left_x), _
gr_height / (bottom_y - top_y))
' object's origin.
gr.TranslateTransform(-left_x, -top_y)
End Sub
=============================
and I call that in the LineObject class (in module):
======================
Public Sub Paint(ByRef g As Graphics) Implements GraphObject.Paint

SetScale(g, FormGraph.DefInstance.pictureBoxGraph.Width, FormGraph.DefInstance.pictureBoxGraph.Height, pointA.X, pointA.Y, pointB.X, pointB.Y)

g.DrawLine(Pens.Black, pointA, pointB)
End Sub
========================
It still does not work !
Please feel free to comment on anything.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 2

Author Comment

by:kouroshparsa
Comment Utility
Hi Idle-Mind. Thanks to your good code.
I finally fixed all the problems myself and I'm posting this in case you may care.
I used to receive errors saying "Engine error" or "invalid parameter"...
The problem was that I disposed the graphic object at the end of the drawing process in the paint sub (in the module).
Normally it is ligal, but since Idle-Mind has used an interface, I should not dispose it !!! The debugging was very tough since it displayed that the error happend in a different place(not in module) !

By the way, in your first code, in the module you had some empty subs:
    Private Sub New()
    End Sub
I ignored that & I'm fine without it...What was your purpose?

Idle-Mind, about the idea of using "paths," I'm not sure how VB.NET manages memory with that. Do you know whether it's safe to have 100 different paths (every path is made by 400 lines)???
Cheers.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
By making the New() sub private, you prevent the user from creating an instance of your application using the default constructor like this:

    Dim c As someClass = New someClass()

You cannot create an instance of a class using the default contructor if it is private.  The reason for doing this is to force the user to pass in some kind of variable:

    Public Class someClass

        Private tpb As PictureBox

        ' Disable default consrtuctor
        Private Sub New()
        End Sub

        ' Force user to create in instance by passing in some kind of parameter
        Public Sub New(ByVal targetPictureBox As PictureBox)
            tpb = targetPictureBox
        End Sub

    End Class

So in the above class you can only create instances that receive a reference to a PictureBox:

    Dim c As someClass = New someClass(PictureBox1)

As far as Paths are concerned, I have never tried using them on the scale you are suggesting.  I used them once in an app that stored objects (lines, polygons, curves, etc) and it worked very well.  At most, I had about 50 objects onscreen at once with between 2 and 15 "lines" per path.

Regards,

Idle_Mind
0
 
LVL 2

Author Comment

by:kouroshparsa
Comment Utility
That is a great explanation. Thanks a lot.
With the idea of using an interface, new issues are occurred for me.
A new problem is streching the picture when resizing...
I would be happy if you take a look at:

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VB_DOT_NET/Q_21098910.html

Cheers
Have fun
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Creating an analog clock UserControl seems fairly straight forward.  It is, after all, essentially just a circle with several lines in it!  Two common approaches for rendering an analog clock typically involve either manually calculating points with…
The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (http://www.ecb.europa.eu/stats/exch…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

771 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now