# VBForums CodeBank > CodeBank - Visual Basic .NET >  Very Simple Drawing Program

## jmcilhinney

C# version here, courtesy of ForumAccount.

The following code can be used as the basis for a simple drawing program.  It assumes that you have a PictureBox on your form named PictureBox1.  The Save and Clear methods can be called from wherever you like.  The most likely candidates would be the Click event handlers of either Buttons or menu items.  I wrote this in VB 2005.  For earlier versions you'd have to use an ArrayList instead of a List(Of Line) or else derive your own LineCollection class from CollectionBase.
VB.NET Code:
Public Class Form1
     'The lines that have been drawn but not saved.
    Private lines As New List(Of Line)
     'The start point of the line currently being drawn.
    Private start As Point
     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Place a blank image in the PictureBox control.
        Me.PictureBox1.Image = New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height)
    End Sub
     Private Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        'Remember the point at which the line started.
        Me.start = e.Location
    End Sub
     Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        'Remember the point at which the line ended.
        Dim [end] As Point = e.Location
         'Add the new line to the list.
        Me.lines.Add(New Line(Me.start, [end]))
         Dim area As New Rectangle(Math.Min(Me.start.X, [end].X), _
                                  Math.Min(Me.start.Y, [end].Y), _
                                  Math.Abs(Me.start.X - [end].X), _
                                  Math.Abs(Me.start.Y - [end].Y))
         'Inflate the rectangle by 1 pixel in each direction to ensure every changed pixel will be redrawn.
        area.Inflate(1, 1)
         'Force the control to repaint so the new line is drawn.
        Me.PictureBox1.Invalidate(area)
        Me.PictureBox1.Update()
    End Sub
     Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        'Draw each line on the control.
        Me.DrawLines(e.Graphics)
    End Sub
     Private Sub Save()
        'Create a Graphics object from the Image in the PictureBox.
        Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
            'Draw each line on the image to make them permanent.
            Me.DrawLines(g)
        End Using
         'Clear the temporary lines that were just saved.
        Me.Clear()
    End Sub
     Private Sub Clear()
        'Clear all unsaved lines.
        Me.lines.Clear()
         'Force the control to repaint so the lines are removed.
        Me.PictureBox1.Refresh()
    End Sub
     Private Sub DrawLines(ByVal g As Graphics)
        For Each line As Line In Me.lines
            g.DrawLine(Pens.Black, line.Start, line.End)
        Next line
    End Sub
 End Class
 Public Class Line
     'The line's start point.
    Private _start As Point
     'The line's end point.
    Private _end As Point
     'The line's start point.
    Public Property Start() As Point
        Get
            Return Me._start
        End Get
        Set(ByVal value As Point)
            Me._start = value
        End Set
    End Property
     'The line's end point.
    Public Property [End]() As Point
        Get
            Return Me._end
        End Get
        Set(ByVal value As Point)
            Me._end = value
        End Set
    End Property
     Public Sub New()
        Me.New(Point.Empty, Point.Empty)
    End Sub
     Public Sub New(ByVal start As Point, ByVal [end] As Point)
        Me._start = start
        Me._end = [end]
    End Sub
 End Class
I strongly recommend that you now go down and read post #17 for some explanation.

----------


## uniquegodwin

Nice tool Jm  :Smilie:

----------


## jameswilson

Excellent post and thanks, i have succesfully modified this to work under the cf which is my aim but im looking for a free hand tool. ie in full .net 2.0 click and draw. This is a line application and does this very well. Is it easy to modify this so that it will be a free hand tool and if so where?

VB Code:
g.DrawLine(pens, line.Start.X, line.Start.Y, line.End.X, line.End.Y)
chnage to the above for cf as it doesnt suppoert .location

----------


## jmcilhinney

> Excellent post and thanks, i have succesfully modified this to work under the cf which is my aim but im looking for a free hand tool. ie in full .net 2.0 click and draw. This is a line application and does this very well. Is it easy to modify this so that it will be a free hand tool and if so where?
> 
> VB Code:
> g.DrawLine(pens, line.Start.X, line.Start.Y, line.End.X, line.End.Y)
> chnage to the above for cf as it doesnt suppoert .location


I've never tried freehand drawing myself but this was the third result returned by a Google search for _freehand drawing ".net"_.

----------


## Crash893

I have been playing around with this for a few days now

How would i draw a rectangle rather than line?

----------


## jmcilhinney

> I have been playing around with this for a few days now
> 
> How would i draw a rectangle rather than line?


If you call the DrawLine method of a Graphics object to draw a line, how would you guess you draw a rectangle?

----------


## Crash893

sorry that did seem a little vague


here is what i have played around with so far



vb Code:
Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
         ' Create pen.
        Dim RedPen As New Pen(Color.Red, 3)
         ' Create rectangle.
        Dim rect As New Rectangle(0, 0, 200, 200)
        e.Graphics.DrawRectangle(RedPen, rect)
          'Draw each line on the control.
        For Each line As Line In Me.lines
            e.Graphics.DrawLine(Pens.Black, line.Start, line.End)
         Next line
     End Sub

the result of which is that it draws a rectangle in the upper right hand side of the screen.

im still a little unsure where the cordnates are read in from the mouse clicks

----------


## jmcilhinney

As you can see from my code I create a Line object from two Points that are read in on the MouseDown and MouseUp events.  Instead of a collection of Line objects you would have a collection of Rectangle objects, or maybe just one Rectangle object.  You can easily create a Rectangle from two Points.  Two Points provides your maximum and minumum X values and Y values.  What else do you need for a Rectangle?  Then you just pass that Rectangle object to DrawRectangle on the Paint event.

----------


## Odin3655

Yet another easy example for a beginner like me.

The only think I haven't figured out is this code:


vb.net Code:
'Remember the point at which the line ended.
Dim [end] As Point = e.Location

Why is the variable end inside brackets?

----------


## jmcilhinney

> Yet another easy example for a beginner like me.
> 
> The only think I haven't figured out is this code:
> 
> 
> vb.net Code:
> 'Remember the point at which the line ended.
> Dim [end] As Point = e.Location
> 
> Why is the variable end inside brackets?


'End' is a reserved word in VB.NET.  Just like in SQL, if you want to use a reserved word as an identifier you have to enclose it in brackets.  If you don't then it will be interpreted as a reserved word and a syntax error will result.  Note that the brackets are not part of the identifier, but just indicate that the enclosed value IS an identifier.  You can use brackets on any and all identifiers if you want, but they only serve a purpose on reserved words.

----------


## Zboy

Is there a way to erase lines you have drawn ?

----------


## jmcilhinney

> Is there a way to erase lines you have drawn ?


What gets drawn is whatever's in the List, so if you don't want a line drawn any more then you need to remove it from the List.

----------


## noahssite

How exactly would i make it freedraw?

You can just draw your mouse around and draw instead of having to release the button on your mouse (mouse up).

----------


## noahssite

> How exactly would i make it freedraw?
> 
> You can just draw your mouse around and draw instead of having to release the button on your mouse (mouse up).


Whell after modifing your code i came up with this:


vb.net Code:
Public Class Form1
     'The lines that have been drawn but not saved.
    Private lines As New List(Of Line)
     'The start point of the line currently being drawn.
    Private start As Point
    Dim [end] As Point
     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Place a blank image in the PictureBox control.
        Me.PictureBox1.Image = New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height)
    End Sub
     Private Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        'Remember the point at which the line started.
        Me.start = e.Location
     End Sub
     Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then
                        Me.start = e.Location
            [end] = New Point(e.Location.X + 1, e.Location.Y + 1)
             'Add the new line to the list.
            Me.lines.Add(New Line(Me.start, [end]))
             Dim area As New Rectangle(Math.Min(Me.start.X, [end].X), _
                                      Math.Min(Me.start.Y, [end].Y), _
                                      Math.Abs(Me.start.X - [end].X), _
                                      Math.Abs(Me.start.Y - [end].Y))
             'Inflate the rectangle by 1 pixel in each direction to ensure every changed pixel will be redrawn.
            area.Inflate(1, 1)
             'Force the control to repaint so the new line is drawn.
            Me.PictureBox1.Invalidate(area)
            Me.PictureBox1.Update()
        End If
    End Sub
     Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
     End Sub
     Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        'Draw each line on the control.
        Me.DrawLines(e.Graphics)
    End Sub
     Private Sub Save()
        'Create a Graphics object from the Image in the PictureBox.
        Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
            'Draw each line on the image to make them permanent.
            Me.DrawLines(g)
        End Using
         'Clear the temporary lines that were just saved.
        Me.Clear()
    End Sub
     Private Sub Clear()
        'Clear all unsaved lines.
        Me.lines.Clear()
         'Force the control to repaint so the lines are removed.
        Me.PictureBox1.Refresh()
    End Sub
     Private Sub DrawLines(ByVal g As Graphics)
        For Each line As Line In Me.lines
            g.DrawLine(Pens.Black, line.Start, line.End)
        Next line
    End Sub
 End Class
 Public Class Line
     'The line's start point.
    Private _start As Point
     'The line's end point.
    Private _end As Point
     'The line's start point.
    Public Property Start() As Point
        Get
            Return Me._start
        End Get
        Set(ByVal value As Point)
            Me._start = value
        End Set
    End Property
     'The line's end point.
    Public Property [End]() As Point
        Get
            Return Me._end
        End Get
        Set(ByVal value As Point)
            Me._end = value
        End Set
    End Property
     Public Sub New()
        Me.New(Point.Empty, Point.Empty)
    End Sub
     Public Sub New(ByVal start As Point, ByVal [end] As Point)
        Me._start = start
        Me._end = [end]
    End Sub
End Class

I modified mouse_move/down/up. Though what i did works, it looks bad if you move your mouse really fast.
Really i just moved the code around in the mouse_up sub.

----------


## jmcilhinney

> How exactly would i make it freedraw?
> 
> You can just draw your mouse around and draw instead of having to release the button on your mouse (mouse up).


No freehand drawing tools handle fast mouse movements all that well.  You can either call DrawEllipse repeatedly and leave gaps between the dots, or else call DrawLine repeatedly and end up with a slightly less curvy curve.  Paint.NET and MS Paint both take the latter approach.

I do plan to extend this example soon to include freehand drawing, rectangles and ellipses, so stay tuned.

----------


## noahssite

So how would i call it repeatidly? Where would i put it in your code?

----------


## jmcilhinney

I thought that I would add a bit of explanation to this to explain why things are done the way they are.

The way GDI+ works, controls get repainted on screen over and over again, sometimes many times in quick succession.  On each of these occasions the control's Paint event is raised.  You need to use the Paint event of the control to perform your own drawing or else, the next time the control is repainted, your drawing will just disappear as the control paints over it.

When you're creating a custom control you should override the OnPaint method and perform your drawing there.  When you're designing a form or user control and you want to add custom drawing to a child control you handle that control's Paint event, then perform your drawing in the event handler.

You may have seen, or heard people talk about, controls flickering on screen.  That's caused by repainting large areas of the control repeatedly and happens because painting on screen is actually quite a slow process.  As such, you should try to do as little of it as possible.

When you make a change that will affect the drawing on the control you need to force it to repaint.  The easiest way to do this is to call the control's Refresh method.  This will cause the control to repaint its entire area.  Sometimes this is necessary.  Note that, in my code above, I call Refresh in the Clear method.  That's because the lines being cleared could be anywhere on the control.

More often than not, though, you will make a change that will only affect a certain area of a control.  In that case, rather than calling Refresh, you should call Invalidate and Update.  It should be noted that Refresh actually calls Invalidate and Update internally.  It calls Invalidate with no parameters though, so it invalidates the entire control.  When you know that you've only changed a specific area you should specify that area when calling Invalidate, by passing a Rectangle or Region.  In my code above, notice that I call Invalidate in the MouseUp event handler and I pass the smallest Rectangle that contains the line to be drawn.

Now, here's something that often trips people up.  I then call Update, which forces the control to repaint, thereby raising the Paint event.  In the Paint event handler, the code will draw over the entire area of the control.  There's no need for you to worry about what area has been invalidated and what has not.  You just draw everything over the entire control.  This creates a picture in memory of your drawing.  The system then determines what parts of that picture will actually get painted to the screen, based on what parts of the control have been invalidated.  Your code in the Paint event handler is executed very quickly.  It's the actual painting of pixels that is slow, so it's that part that needs to be kept to a minimum.

Note that it is often worth writing relatively complex code to determine the smallest area possible to invalidate because even that complex calculation will take less time than it would to paint the extra pixels that your calculation excludes.  As such, it may even have proven to be more efficient in my code to create a Region that was a tighter fit to the line being drawn than a Rectangle that, obviously, will have a fair bit of area in it not occupied by the line.  I didn't do that because I was just trying to illustrate a point, but in applications that do a lot of drawing and repainting it would likely be worthwhile.

----------


## noahssite

Thankyou jmc although i said i understand perfectly in my thread i only understood that code and such... now i understand GDI which will come in handy..

So a program like MSPaint would override the OnPaint method?

But MSPaint doesnt flicker at all and you can draw freely all over.. would that be because it would only invalidate a small region?

----------


## jmcilhinney

> Thankyou jmc although i said i understand perfectly in my thread i only understood that code and such... now i understand GDI which will come in handy..
> 
> So a program like MSPaint would override the OnPaint method?
> 
> But MSPaint doesnt flicker at all and you can draw freely all over.. would that be because it would only invalidate a small region?


Paint isn't a .NET application so it wouldn't use this technique, but if you were to create a Paint-like program (which this is a very simple version of) then yes, you would always invalidate the smallest area possible to minimise the area that gets repainted and thereby minimise the chance of flicker.  You can also use double-buffering on the form to reduce flicker even more.

----------


## LoGiCAnti

how would i be able to place the drawing with a (real) image in the picturebox and be able to save over the loaded image? (save both image and drawing?)

----------


## jmcilhinney

> how would i be able to place the drawing with a (real) image in the picturebox and be able to save over the loaded image? (save both image and drawing?)


The whole purpose of this thread is to illustrate how to do exactly that.  In my example I use a blank Image.  If you don't want to use a blank Image then don't use a blank Image.  Use some other Image instead.

----------


## [F]ufu

nc jmcilhinney  :Thumb: 

but i wonder can you save it in database after your draw in picturebox???? tnx

----------


## jmcilhinney

> nc jmcilhinney 
> 
> but i wonder can you save it in database after your draw in picturebox???? tnx


An Image is an Image, regardless of how it's created.  You would save an Image created like this the same way as you would any other.  Follow the CodeBank link in my signature below and check out my thread on Saving Images To Databases.

----------


## rinkley

I know it has been a long time since anything was added to this discussion, but I can't think of a better place to put this...

I used your "simple drawing" example to solve a problem I had with creating images in a picturebox.  The application is working very well and I thank you for that.

Now I would like to save the contents of the picturebox to a disk file.  It looks like I should be able to add a line in your Save routine like:

Me.Picturebox1.Image.Save(<path and filename>, System.Drawing.Imaging.ImageFormat.Jpeg)

and call the routine from a button click, but the image comes out blank.  Can you tell me what I am missing?  How would I save the contents of the picturebox to disk?

----------


## jmcilhinney

> I know it has been a long time since anything was added to this discussion, but I can't think of a better place to put this...
> 
> I used your "simple drawing" example to solve a problem I had with creating images in a picturebox.  The application is working very well and I thank you for that.
> 
> Now I would like to save the contents of the picturebox to a disk file.  It looks like I should be able to add a line in your Save routine like:
> 
> Me.Picturebox1.Image.Save(<path and filename>, System.Drawing.Imaging.ImageFormat.Jpeg)
> 
> and call the routine from a button click, but the image comes out blank.  Can you tell me what I am missing?  How would I save the contents of the picturebox to disk?


I just did a quick test and saw a similar result, although I'm not sure why to be honest.  I'll have to do a bit of investigation and get back to you on that.

----------


## rinkley

Thank you for your quick reply.  I am honored.
After more investigation, I found the solution.  I don't know enough about what I am doing to explain why it works, but it turns out that adding Me.Picturebox1.Refresh immediately before saving the image to disk takes care of the problem.

----------


## Alain

Hi all,

I'd like to through in a little extra challenge to this nice little vectorial drawing example app.  In this current example, it is not possible to move, modify or delete drawn objects (line, rectangle, ellipse, whatever...).  Although being able to modify these objects would imply lots of extra code, the first step is to add some code that will allow users to 'select' drawn objects.  That's what I'm interested in.

I seriously gave a lot of thoughts about that, and so far what I came up with is not simple, nor very performant I'm afraid.  The source of the problem is to define an efficient way to find out if the mouse cursor if over a drawn object, and which one.  Unless the GDI+ has some feature I don't know about the addresses just that (which may be possible, given the fact I'm just starting with VB.NET...).

Any thoughts?

Thanks!

----------


## Gruff

Hope you don't mind JM.

Alain:  

Selections routines entail searching through your list(of clsDrawingObjects)
For line objects you generally want to use a closest point perpendicular to the line formula.  
Search within a predetermined selection tolerance.  You also have to determine if the selection
is within the length of the line.

If you implement real world drawing units and zoom and pan the selection tolerance needs to be translated into screen coordinates so that that selection distance to the object always appears the same.

Every drawing object type will need it's own selection formula.

In short this is typically not a trivial thing to do.

If you want to pursue this further here is a sub-routine that calculates the closest perpendicular distant from a cursor point to a line.  The OnPt parameter is the intersection point on the line selected.
With this you can check if the OnPt is inside the line endpoints.

The hypotnuse function is not provided, but should not be difficult to create.



```
  Public Function Closest_Dist(ByVal Pt As PointF, ByVal sPt As PointF, ByVal ePt As PointF, ByRef OnPt As PointF) As Single
    'Task:  Given a line(sPt, ePt) and a point(Pt)
    '       Determine the closest distance between the line and the point.

    '       Although the line is defined by a pair of x,y points, this function makes no
    '       such distinction. The line is assumed infinite in length.

    'Step 1 Check if the line is vertical or horizontal.  Solve if true.
    'Step 2 Determine a line perpendicular to the existing line and through the point.
    'Step 3 Determine the intersection point of both lines.

    Dim Sab, Scd, Iab, Icd, XDist, YDist, Dist As Single

    If IsEqual(sPt.X, ePt.X) Then
      Dist = Math.Abs(sPt.X - Pt.X) 'Line is vertical
      OnPt.X = sPt.X 'Line is vertical
      OnPt.Y = Pt.Y
    ElseIf IsEqual(sPt.Y, ePt.Y) Then
      Dist = Math.Abs(sPt.Y - Pt.Y) 'Line is horizontal
      OnPt.X = Pt.X 'Line is horizontal
      OnPt.Y = sPt.Y
    Else
      Sab = (sPt.Y - ePt.Y) / (sPt.X - ePt.X)  'Slope of the existing line
      Iab = sPt.Y - (Sab * sPt.X)        'Y intercept of the existing line

      Scd = -(1 / Sab)             'Slope of the calculated the line (Inverse of existing.)
      Icd = Pt.Y - (Scd * Pt.X)          'Y intercept of calculated line

      OnPt.X = CSng((Icd - Iab) / (Sab - Scd)) 'Calculate Intersection point of both lines.
      OnPt.Y = CSng((Sab * OnPt.X) + Iab)

      XDist = Math.Abs(OnPt.X - Pt.X)            'Calculate X and Y Spacing
      YDist = Math.Abs(OnPt.Y - Pt.Y)

      'Find the true distance (Hypotnuse of Xdist and Ydist).
      Dist = CSng(Hypotnuse(XDist, YDist)) 'Hypotnuse of Xdist and Ydist.
    End If

    Return Dist
  End Function
```

----------


## Alain

> In short this is typically not a trivial thing to do.


I couldn't agree more!

I was working on such an issue years ago, and didn't find a viable solution until I put this aside (it was just for fun, outside of my work).  I tried the implement the proceedure you described, and wasn't able to do so it worked efficiently.  

I had a conversation with a coworker (in a past job) who implemented such a thing.  Without getting into details, he explained that every object on a drawing had it's phantom collection of small rectangles that were overlapping every drawings (which were all extensions of the polyline object).  Then when a user clicked somewhere, the process had to check if he cliked within a rectangle, and if so then select the associated drawing object.  If I remember well, an important part of the process was to store the coordinates of the recangles in some sort of hash table (or something), in order to avoid evaluating each and every rectangle to see if the user clicked in it.  But since we didn't get into details (which I regret we didn't today!), I'm stuck reinventing the well (and asking you guys if you wish to play with me at the same time!   :Smilie:   )

Here's an small drawing (done with MS Paint, so bare with me...) illustrating the phantom rectangle thing.



The downside about this method is that you have to recode every GDI drawing function, in order to use the polyline instead to make lines, rectangles, circles and stuff...


EDIT: Thanks for your code Gruff.  I was typing as you edited your post.  I'll check it out.

----------


## Gruff

Circles are actually easier to detect than lines.
You derive the Distance of the cursor location to the circle center point.


```
  Dist = Hypotnuse(DeltaX, DeltaY)
  Gap = ABS(Dist-Radius)
  if Gap <= SelectionTol then
    'Add entity to selection list here
  End If
```

----------


## boops boops

Hi Alain,

to do hit testing on a graphic line, use a Drawing2D.GraphicsPath. In other words, instead of drawing the rectangle, arc etc. with commands like Graphics.DrawArc, do something like this:


```
'delcare the GraphicsPath object:
Private gp As New Drawing2D.GraphicsPath

'build the path:
gp.AddArc(ellipseRectangle, startAngle, sweepAngle)

'draw the path e.g. in the Paint event handler:
e.Graphics.DrawPath(pens.Black, gp)
```

The point of this is that you can use the GraphicsPath.IsVisible(_point_) and GraphicsPath.IsOutlineVisible(_point_, _pen_) methods for hit testing. IsVisible tests if the point is contained in a closed path e.g. an ellipse. IsOutlineVisible tests if the point is approximately on the line. The _pen_ argument is used to specify the thickness of the hit testing area along the line, plus some other details; the pen colour is irrelevant).

The advantage of these methods is that they work for any kind of drawing. Anything you can draw in a Graphcs statement (except DrawImage) you can also add to a GraphicsPath. So you can do your hit testing in a few statements on lines, polylines, rectangles, ellipses, arcs, Bézier curves, cubic splines, typographic text and a few other things too; plus any combination of these into a single figure. Try doing that with mathematical formulas :Wink: . 

The hit testing probably boils down to something like the tiny rectangles you illustrated or formulas like Gruff's at a lower level. But there is no need to do it yourself.

By the way, all this is going way of topic from the Very Simple Drawing Program JMcIlhinney offered to help beginners. If you have further questions, it would be better to post them either to the VB.Net Forum or the Games and Graphics forum.

BB

----------


## Alain

Hi Boops,

Thanks a lot for letting me know about the GraphicsPath class (or namespace, I'm not sure which word to use, still learning .NET.).  This is obviously the way to go to get the result I'm after.

And yes, you're right, my question was making a 'very simple application' quite a bit more complex.  Thus I should have asked the question in the standard VB.NET forum.  Will do for any further inquiery about this.

And sorry for not getting back earlier, I'm on vacation!

Thanks again, and have a great day!

----------


## user20

> C# version here, courtesy of ForumAccount.
> 
> The following code can be used as the basis for a simple drawing program.  It assumes that you have a PictureBox on your form named PictureBox1.  The Save and Clear methods can be called from wherever you like.  The most likely candidates would be the Click event handlers of either Buttons or menu items.  I wrote this in VB 2005.  For earlier versions you'd have to use an ArrayList instead of a List(Of Line) or else derive your own LineCollection class from CollectionBase.
> VB.NET Code:
> Public Class Form1
>      'The lines that have been drawn but not saved.
>     Private lines As New List(Of Line)
>      'The start point of the line currently being drawn.
>     Private start As Point
> ...


Finally I found this! I have a question about save image in picturebox.
I loaded an image in picturebox and drew afew lines on picturebox. now I want to save the image and drawn lines in a path.
if use this code:


```
picturebox1.image.save(PATH, Imaging.ImageFormat)
```

only saves the image that loaded in picturebox without drawn lines

Please guide me and please don't say "check out my thread on Saving Images To Databases"! Because I read all of that but didn't get answer. thnx

----------


## jmcilhinney

@user20, my example demonstrates what you need to do. What part do you not understand? Have you actually tested this code in the debugger to see how it works, or are you assuming that it should just work without any effort on your part. You need to make an effort here. I wrote this code to demonstrate the principle so that people like you could see, understand it and then apply it themselves. I see no evidence that you've tried to understand it. If you have a specific question about my code, I'm happy to answer it. If you're just going to ignore the example and expect me to write your code for you, you'll be disappointed.

----------


## wqweto

> if use this code:
> 
> 
> ```
> picturebox1.image.save(PATH, Imaging.ImageFormat)
> ```
> 
> only saves the image that loaded in picturebox without drawn lines


What you are looking for is literally in the code you are quoting in the *Save* procedure



```
    Private Sub Save()
        'Create a Graphics object from the Image in the PictureBox.
        Using g As Graphics = Graphics.FromImage(Me.PictureBox1.Image)
            'Draw each line on the image to make them permanent.
            Me.DrawLines(g)
        End Using
 
        'Clear the temporary lines that were just saved.
        Me.Clear()
    End Sub
```

Call this procedure to "save" the lines buffered up till now inside the *PictureBox1.Image* before saving this *PictureBox1.Image* in a file using your (confusingly named) *PATH* variable the way you do.

cheers,
</wqw>

----------


## user20

Thanks guys for the help
I add two button in form for zoom in and zoom out. 


```
   Private Sub Button_zoom_out_Click(sender As Object, e As EventArgs) Handles Button2.Click

        PictureBox1.Width += Convert.ToInt32(PictureBox1.Width * -50 / 1000)
        PictureBox1.Height += Convert.ToInt32(PictureBox1.Height * -50 / 1000)
        PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage

        For Each line As Line In lines
            line.Start = New Point(Convert.ToInt32(line.Start.X * (1 - 50 / 1000)), Convert.ToInt32(line.Start.Y * (1 - 50 / 1000)))
            line.End = New Point(Convert.ToInt32(line.End.X * (1 - 50 / 1000)), Convert.ToInt32(line.End.Y * (1 - 50 / 1000)))
        Next
    End Sub

    Private Sub Button_zoom_in_Click(sender As Object, e As EventArgs) Handles Button2.Click

        PictureBox1.Width += Convert.ToInt32(PictureBox1.Width * 50 / 1000)
        PictureBox1.Height += Convert.ToInt32(PictureBox1.Height * 50 / 1000)
        PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage

        For Each line As Line In lines
            line.Start = New Point(Math.Round(line.Start.X * (1 + 50 / 1000)), Convert.ToInt32(line.Start.Y * (1 + 50 / 1000)))
            line.End = New Point(Convert.ToInt32(line.End.X * (1 + 50 / 1000)), Convert.ToInt32(line.End.Y * (1 + 50 / 1000)))
        Next
    End Sub
```

After zoom in or zoom out the image, if we save it, the lines will be moved. picturebox.sizemode is  AutoSize .
Attachment 186416

----------


## jmcilhinney

> picturebox.sizemode is  AutoSize .


Except it's not, because you're changing it to StretchImage in the code you just posted. If the Image is not displayed with its original size or aspect ratio or its origin is not coincident with the control then you can't just do the same drawing on both and expect it to look the same. You would need to work out the maths required to translate from one to the other.

----------


## user20

> Except it's not, because you're changing it to StretchImage in the code you just posted. If the Image is not displayed with its original size or aspect ratio or its origin is not coincident with the control then you can't just do the same drawing on both and expect it to look the same. You would need to work out the maths required to translate from one to the other.


I change Sizemode of picturebox to StretchImage But the problem was not solved. Please If my codes are not correct , edit them for me.

----------


## jmcilhinney

> I change Sizemode of picturebox to StretchImage But the problem was not solved.


Why would that solve the problem when I just told you that it's the cause?



> Please If my codes are not correct , edit them for me.


No. Code doesn't just appear out of thin air. You work out the logic first, then you write code to implement the logic. If you could specify what the logic was and then specify exactly where you're having trouble writing code to implement it then I might provide that code. You're e4xpecting others to work out the logic to implement and the code to implement it though. I'm not here to do that. I've told you what you need to do: work out the maths needed to translate from one coordinate system, i.e. the PictureBox, to another coordinate system, i.e. the Image. That's not a programming problem so you don't need to have programming experience to solve it. You just need a basic understanding of maths and the will to make the effort. Until I see that effort, i will not be contributing further.

----------


## wqweto

> I change Sizemode of picturebox to StretchImage But the problem was not solved. Please If my codes are not correct , edit them for me.


Your codes are completely wrong. You'll have to start over.

First try to implement zooming in/out the image alone without the additional lines drawn upon it.

Can you implement saving zoomed images based on some scale variable e.g. scale = 0.5 for half the size, scale = 2.0 for double the original size?

cheers,
</wqw>

----------


## user20

> Can you implement saving zoomed images based on some scale variable e.g. scale = 0.5 for half the size, scale = 2.0 for double the original size?


Yes, not problem. I will try to implement that.

----------


## passel

If you want to implement zooming, I would suggest using the ScaleTransform method of the Graphics Class to do the scaling and avoid all the math coordinate calculations and manipulation for your drawing.
If you draw the image and then all the lines in whatever coordinate system you choose, you can scale the drawing up or down or even rotated without having to modify the coordinates of your drawing at all.

This post includes a couple of links that references a number of attachments with various examples of drawing situations. In particular, the "VB_Drawing_Demo" and the "PaperPuppetExample" both make use of the ScaleTransform to increase or decrease the size of the drawing without having to change any of the core drawing routines at all.

----------

