[Visualbasic 2010] Redraw lines after form refresh

Discussion in 'Mixed Languages' started by stevemk14ebr, Dec 19, 2011.

  1. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    #1 stevemk14ebr, Dec 19, 2011
    Last edited by a moderator: Apr 20, 2017
    i have this code to make a grid(in a timer):
    Code:
    dim g as graphics
    g=me.creategraphics
     For x = 0 To 19
                For y = 0 To 14
                    r = New Rectangle(x * 32, y * 32, 32, 32)
                    g.DrawRectangle(Pens.Black, r)
                Next
            Next
    then this code to turn that grid off:
    Code:
    crtgrid.Enabled = False
                Me.Refresh()
    then this code to draw a line
    Code:
                Dim p As New Pen(Color.Black, 3)
                g.DrawLine(p, curline.startpoint, curline.endpoint)
               
            
    after i call form refresh my line dissapears, i know i will be using multiple lines and i need help in finding a way to store all the lines i draw into a collection then loop through the collection after a form refresh to redraw the lines help:confused:
     
  2. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    anybody :(
     
  3. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    I'm guessing that you're obtaining a reference to the Graphics object outside of the OnPaint event. You have to make sure that the line gets drawn every time you call Me.Refresh() if you want it to persist. You would generally accomplish this by overriding the control's OnPaint method. See MSDN for an example.

    As a rule of thumb, do all required drawing inside the OnPaint event.

    ;)
     
  4. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    #4 stevemk14ebr, Dec 24, 2011
    Last edited by a moderator: Apr 20, 2017
    (OP)
    see thats my problem the line is created in runtime by the user and i can't put a line in the on paint event for every line the user could possibly make, so i have to create a new line for every line the user draws outside of that and that works up until i refresh the form. I know it is possible to store those lines in some sort of group of points so that when a new line is made it's points are stored in the group of points,then when i refresh the form call on the group of points to loop through and redraw all the lines

    P.S i almost have a working example but it connects all the lines instead of individually:
    Code:
    (declares)
     Private lines As New List(Of Point)
     Private lines2 As New List(Of Point)
    
    (code to add points to list)
     lines.Add(pnt1)
     lines2.Add(pnt2)
    
    (loop in a timer)
     Dim pen As New Pen(Color.Black, 3)
            For Each line As Point In lines
                For Each line2 As Point In lines2
                    g.DrawLine(pen, line, line2)
                    Next
            Next
    
    and ends up with 2 extra lines like i showed in picture in attachment
    Capture.JPG
     
  5. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    Ah that explains it :) Maybe you could have a Bitmap in-memory, and duplicate the drawings on both the Bitmap and the Form surface, so that when you call Me.Refresh() you will DrawImage() the Bitmap back onto the Form surface.
     
  6. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    ah thats an interesting idea, so i take a pic of my form before refresh then refresh then redraw bitmap onto form then coninue drawing lines is that what u meen:D
     
  7. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    Yeah something along those lines ;)

    You could also create a Bitmap and when the user draws something on the Form, you could duplicate it immediately to the Bitmap, and in OnPaint just replace the whole Form surface with the Bitmap.

    This way or the other! Tell us how you solved it when you're done (for science) :D
     
  8. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    ok ill probably implement it this week been playing bf3 :D
     
  9. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    #9 stevemk14ebr, Dec 28, 2011
    Last edited by a moderator: Apr 20, 2017
    (OP)
    ok here it is:
    Code:
     'when user clicks refresh button change border style,create blank bitmap,initialize graphics object for screenshot,save screenshot to empty
     'bitmap,invalidate form,set form background as bitmap with screenshot,change border back, DONE!
     
    'changing border so that it doesn't appear in screenshot
    Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
    Dim bounds As Rectangle = Me.Bounds
    Dim screenshot As New Bitmap(bounds.Width, bounds.Height, Imaging.PixelFormat.Format32bppArgb)
    Dim g2 As Graphics = Graphics.FromImage(screenshot)
    g2.CopyFromScreen(bounds.Location, New Point(0, 0), bounds.Size, CopyPixelOperation.SourceCopy)
    Me.Refresh()
    Me.BackgroundImage = screenshot
    'changing border back
    Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Sizable
    And the result(after form refresh) :
    Capture.JPG
     
  10. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    Very nice, well done. I think you can skip the FormBorderStyle change and instead use the values provided by Me.ClientRectangle to ignore the border (I believe that's how it worked at least). Btw, if you want smoother lines you can set the SmoothingMode property of the Graphics object to for example AntiAlias :)
     
  11. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    ok ill try that thanks for the input :D
     
  12. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    #12 stevemk14ebr, Dec 29, 2011
    Last edited by a moderator: Apr 20, 2017
    (OP)
    OK finished with complete ideas, tried the client rectangle thing and that didn't work so i ended up just changing the starting point to the point without border and changes smoothing mode to high-quality:
    Code:
    Dim pen As New Pen(Color.Black, 3)
            'change smooothign to hq, drawing line on form with pen point 1 and point 2
            g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
            g.DrawLine(pen, pnt1, pnt2)
    Code:
    Private Sub refresh1_Click(sender As System.Object, e As System.EventArgs) Handles refresh1.Click
            'when user clicks refresh button,create variable with form bounds,create point without border,create blank bitmap,initialize graphics object for screenshot,save screenshot to empty
            'bitmap,invalidate form,set form background as bitmap with screenshot,change border back, DONE!
    
            Dim bounds As Rectangle = Me.Bounds
            'hiding the border by changing starting point + or - border width
            Dim strt As Point = New Point(Me.Bounds.Location.X + 8, Me.Bounds.Location.Y + 30)
            Dim screenshot As New Bitmap(bounds.Width, bounds.Height, Imaging.PixelFormat.Format32bppArgb)
            Dim g2 As Graphics = Graphics.FromImage(screenshot)
            g2.CopyFromScreen(strt, New Point(0, 0), bounds.Size, CopyPixelOperation.SourceCopy)
            Me.Refresh()
            Me.BackgroundImage = screenshot
    
        End Sub
    Capture.JPG

    If you want to look at the whole project i uploaded it to megaupload, p.m me if u can't access it: http://www.megaupload.com/?d=7W1TF9TQ
     
  13. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    #13 Calistoga, Dec 29, 2011
    Last edited by a moderator: Apr 20, 2017
    Thanks for posting your solution :)

    I thought I should just mention an alternative method that came to mind (no need to change yours though).

    First, create a class called Line (C#, it's so long since I've worked with VB):
    Code:
    class Line
    {
         Line m_start, m_end;
    
         Public Line(Point start, Point end)
         {
               this.m_start = start;
               this.m_end = end;
         }
    
         public Point Start
         {
              get { return this.m_start; } 
              set { this.m_start = value; }
         }
    
         public Point End
         {
              get { return this.m_end; } 
              set { this.m_end = value; }
         }
    }
    
    In the Form1 class you could then have Dim m_lines As List<Line>

    Now in the MouseUp event you could store the new line as follows:
    Code:
    Dim line As New Line(pnt1, pnt2)
    
    Me.m_lines.Add(line)
    
    Then you could just loop through all the Line's and draw them manually in OnPaint.

    Sorry if this is confusing, I started out thinking in C#, then I realized this is VB.NET, but yeah :) The logic is, store input as Line, store each Line in a List, draw Line's in OnPaint.

    (I will probably never write an image editor :p)
     
  14. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    i kinda understand what ur saying(i can follow c# but barely) my original problem was i couldn't figure out how to store the line into a list of lines then loop though the list if u can tell me how to do that i would love u (1. create a list of lines, 2.loop through the list)

    P.S i could almost make the list but i just couldnt get the syntax right (first time working with lists)
     
  15. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    #15 Calistoga, Dec 31, 2011
    Last edited by a moderator: Apr 20, 2017
    Sorry for the slow reply ;)

    This is (theoretically speaking) the correct VB.NET code for the Line class:
    Code:
    Class Line
        Private m_start As Point, m_end As Point
    
        Public Sub New(start As Point, [end] As Point)
            Me.m_start = start
            Me.m_end = [end]
        End Sub
    
        Public Property Start() As Point
            Get
                Return Me.m_start
            End Get
            Set(value As Point)
                Me.m_start = value
            End Set
        End Property
    
        Public Property [End]() As Point
            Get
                Return Me.m_end
            End Get
            Set(value As Point)
                Me.m_end = value
            End Set
        End Property
    End Class
    
    To create a Line:
    Code:
    Dim line As Line = New Line(p1, p2) ' Where p1 and p2 is two Points.
    Now to store a list of the Lines you need List<T> from the System.Collections.Generic namespace.
    Code:
    Imports System.Collections.Generic ' Place this in the top of your code file
    Code:
    Dim list As List(Of Line) = New List(Of Line) ' Refer to the "list" from here on
    Add Lines to the List by issuing list.Add(line)

    If something here makes no sense I probably wrote something wrong, so report back in that case ;)
     
  16. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    ok ill try that some time this weekend have some other work to do for now, thanks for your time :D
     
  17. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    #17 stevemk14ebr, Dec 31, 2011
    Last edited by a moderator: Apr 20, 2017
    (OP)
    Ok finished what you suggested and it worked perfectly,exactly what i was looking for originally(All i did was add a for loop on form paint):
    Code:
    Private Sub Form1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
            For Each L As Line In list
                Dim pen As New Pen(Brushes.Black, 3)
                g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
                g.DrawLine(pen, L.Start, L.End)
            Next
        End Sub
    ^ of course declares for g are:
    Code:
    Dim g As Graphics
    (in form load)
    g=me.creategraphics
    Link to project: http://www.megaupload.com/?d=3ESXYTTA
    THANKS MAN I THINK I'VE GOT EVERYTHING I NEED :D:worthy:
    P.S i've got a new problem i started a new thread (About clearing object made in runtime and restoring form to original state before objects were made)
     
  18. Calistoga

    Calistoga MDL Senior Member

    Jul 25, 2009
    421
    199
    10
    Glad you got it solved mate :biggrin:

    One thing that you might find useful for other projects: If you haven't already noticed, you can also Refresh a specific region of a Control as well, by using Me.Invalidate(rectangle) where rectangle is the part of the Control you want to redraw. Very useful when you have to refresh a specific area very fast but do not want to be too heavy on the CPU (remember that the kind of drawing we're doing here is handled by the processor, and not the graphics card). Me.Invalidate() is the exact same thing as Me.Refresh().

    ;)
     
  19. stevemk14ebr

    stevemk14ebr MDL Senior Member

    Jun 23, 2010
    267
    48
    10
    yea i know i used that when i had to draw the score of a game i made onto the screen very fast,but thanks anyways.