Solved

Undo for Picturebox Draw Control

Posted on 2006-10-22
8
1,998 Views
Last Modified: 2008-02-01
What I want to do is add a button "btnUndo" that will undo the last draw operation on this control.  I tried creating an array that will save the current image after each operation and the undo button will revert to it, but I had massive memmory problems with some of the larger images I have tried to edit.  Here is the code I have.

[code]

    Private bitC As New Bitmap(Gadler2.My.Resources.g)
    Private gC As Graphics = Graphics.FromImage(bitC)
    Private brC As New Pen(Color.Red)
    Private dwnC As Boolean
    Private lstC As Point
    Private firstrunC As Boolean = True
    Private PenColorC As String

    Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        If btnBlue.Checked = True Then
            PenColorC = "Blue"
            dwnC = True
        End If

        If btnBlack.Checked = True Then
            PenColorC = "Black"
            dwnC = True
        End If

        If btnRed.Checked = True Then
            PenColorC = "Red"
            dwnC = True
        End If

        If btnGreen.Checked = True Then
            PenColorC = "Green"
            dwnC = True
        End If

        If btnEraser.Checked = True Then
            PenColorC = "White"
            dwnC = True
        End If
    End Sub

    Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        If dwnC = True Then
            Dim sC As Integer
            Dim xyC As Point
            gC.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
            sC = 2
            brC.Color = Color.FromName(PenColorC)
            brC.Width = 2
            If PenColorC = "White" Then brC.Width = 8
            xyC.X = e.X
            xyC.Y = e.Y
            If firstrunC = True Then
                lstC = xyC
                firstrunC = False
            End If
            gC.DrawLine(brC, lstC, xyC)
            lstC = xyC

            PictureBox1.Image = bitC

        End If
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        dwnC = False
        firstrunC = True
    End Sub

[/code]

0
Comment
Question by:rbradberry
  • 4
  • 3
8 Comments
 
LVL 10

Expert Comment

by:Kinger247
Comment Utility
Save the coordinates in the array not the image ..!
That way you redraw the previous coordinates.

0
 
LVL 3

Author Comment

by:rbradberry
Comment Utility
but if i redraw the prvious coordinates in a different color then i am not placing the image back to the way it was, for instance if I cross over 3 different color lines then i would have to somehoe memorize what that pixel color was.  i don't know
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Play with the code below...

It has an ArrayList "paths" that stores each "drawing operation" in it.  To remove the last operation, simply remove the Last GraphicsPath object in the list.  Removing the objects in reverse until it is empty undoes all operations.  Obviously this version isn't storing color by I think you get the idea.  To make it more complex you would make a custom class to store the GraphicsPath, Color, Width, etc, and store instances of that class in the ArrayList instead.  Then in the Paint event you extract the info about each drawing before painting that piece...

Public Class Form1

    Private paths As New ArrayList
    Private gp As System.Drawing.Drawing2D.GraphicsPath
    Private lastPoint As Point

    Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left Then
            gp = New System.Drawing.Drawing2D.GraphicsPath
            paths.Add(gp)
            lastPoint = New Point(e.X, e.Y)
        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 = Windows.Forms.MouseButtons.Left Then
            Dim curPoint As New Point(e.X, e.Y)
            gp.AddLine(lastPoint, curPoint)
            lastPoint = curPoint
            PictureBox1.Refresh()
        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 = Windows.Forms.MouseButtons.Left Then
            PictureBox1.Refresh()
        End If
    End Sub

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        Dim gp As System.Drawing.Drawing2D.GraphicsPath
        Dim g As Graphics = e.Graphics
        For Each gp In paths
            g.DrawPath(Pens.Black, gp)
        Next
    End Sub

End Class
0
 
LVL 3

Author Comment

by:rbradberry
Comment Utility
this seems like a viable solution, however, the lines bieng drawn are not altering the underlying image so when i save the image the changes aren't save, also when i change the pencolor it changes the color of all the lines, not jsut the last one drawn.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
"the lines bieng drawn are not altering the underlying image so when i save the image the changes aren't save"

Correct.  To save the image you first make a copy of the base image and get a graphics to it.  Then you paint the lines in the same manner that the Paint() event does and save the final image.

Something like:

    ' somewhere else you have your "base" image that you set the PictureBox.Image prop to:
    Dim yourOriginalImageInstanceHere As Image = Image.FromFile("c:\someImageFile.bmp")

    ' make a copy of the "base" and get a graphics to it
    Dim newBmp As New Bitmap(yourOriginalImageInstanceHere)
    Dim G As Graphics = Graphics.FromImage(newBmp)
    ' draw all the lines on the copy
    For Each gp As System.Drawing.Drawing2D.GraphicsPath In paths
        G.DrawPath(Pens.Black, gp)
    Next
    ' cleanup and save the new composite image
    G.Dispose()
    newBmp.Save("c:\someImage.bmp")

"when i change the pencolor it changes the color of all the lines, not jsut the last one drawn"

True.  I already said that the example I gave was a simple case and that it would need modification to handle multiple colors/widths.

Would you like to see a more sophisticated example?

By the way, what version VB are you working with here?  2003 or below? ...or 2005?
0
 
LVL 3

Author Comment

by:rbradberry
Comment Utility
sure, i would love to see a better example.  i am working with VB 2005.  Thank you so much.
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 500 total points
Comment Utility
Here is a more sophisticated example...

Add the code below to a blank form.  Double click the red square to change pen color.

Public Class TestForm

    Private Class Squiggle
        Public C As Color
        Public W As Integer
        Public GP As System.Drawing.Drawing2D.GraphicsPath

        Public Sub New(ByVal SquiggleColor As Color, ByVal SquiggleWidth As Integer)
            Me.C = SquiggleColor
            Me.W = SquiggleWidth
            GP = New System.Drawing.Drawing2D.GraphicsPath
        End Sub
    End Class

    Private Squiggles As New ArrayList
    Private curSquiggle As Squiggle = Nothing
    Private lastPoint As Point = Nothing

    Private WithEvents PB As New PictureBox
    Private WithEvents OpenImageBtn As New Button
    Private WithEvents CurColor As New PictureBox
    Private WithEvents CurWidth As New NumericUpDown
    Private WithEvents SaveImageBtn As New Button
    Private WithEvents UndoBtn As New Button
    Private WithEvents ClearBtn As New Button

    Private Sub TestForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim sc1 As New SplitContainer
        sc1.Dock = DockStyle.Fill
        sc1.Orientation = Orientation.Vertical
        sc1.FixedPanel = FixedPanel.Panel1
        sc1.SplitterDistance = 90
        sc1.IsSplitterFixed = True
        sc1.Panel1.Padding = New Padding(5)

        Dim fl1 As New FlowLayoutPanel
        fl1.Dock = DockStyle.Fill
        sc1.Panel1.Controls.Add(fl1)
        fl1.FlowDirection = FlowDirection.TopDown

        OpenImageBtn.Width = 75
        OpenImageBtn.Text = "Open Image"
        fl1.Controls.Add(OpenImageBtn)

        CurColor.Size = New Size(75, 75)
        CurColor.BorderStyle = BorderStyle.FixedSingle
        CurColor.BackColor = Color.Red
        fl1.Controls.Add(CurColor)

        CurWidth.Width = 75
        CurWidth.Minimum = 1
        CurWidth.Maximum = 7
        fl1.Controls.Add(CurWidth)

        SaveImageBtn.Width = 75
        SaveImageBtn.Text = "Save Image"
        fl1.Controls.Add(SaveImageBtn)

        UndoBtn.Width = 75
        UndoBtn.Text = "Undo"
        fl1.Controls.Add(UndoBtn)

        ClearBtn.Width = 75
        ClearBtn.Text = "Clear"
        fl1.Controls.Add(ClearBtn)

        PB.Dock = DockStyle.Fill
        sc1.Panel2.Controls.Add(PB)

        Me.Controls.Add(sc1)
    End Sub

    Private Sub OpenImageBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles OpenImageBtn.Click
        Dim ofd As New OpenFileDialog
        ofd.Title = "Open Image"
        ofd.Filter = "Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*"
        If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
            Try
                ' open image without a file lock
                Dim img As Image = Image.FromFile(ofd.FileName)
                PB.Image = New Bitmap(img)
                img.Dispose()
            Catch ex As Exception
                MessageBox.Show(ofd.FileName & vbCrLf & vbCrLf & ex.Message, "Error Opening File", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End If
    End Sub

    Private Sub CurColor_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles CurColor.DoubleClick
        Dim cd As New ColorDialog
        If cd.ShowDialog = Windows.Forms.DialogResult.OK Then
            CurColor.BackColor = cd.Color
        End If
    End Sub

    Private Sub SaveImageBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SaveImageBtn.Click
        Dim sfd As New SaveFileDialog
        sfd.Title = "Save Image"
        sfd.Filter = "Bitmaps (*.bmp)|*.bmp"
        If sfd.ShowDialog = Windows.Forms.DialogResult.OK Then
            Dim bmp As New Bitmap(PB.Image)
            Dim g As Graphics = Graphics.FromImage(bmp)
            PaintSquiggles(g)
            g.Dispose()
            bmp.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Bmp)
        End If
    End Sub

    Private Sub UndoBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UndoBtn.Click
        If Squiggles.Count > 0 Then
            Squiggles.RemoveAt(Squiggles.Count - 1)
            PB.Refresh()
        End If
    End Sub

    Private Sub ClearBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ClearBtn.Click
        If Squiggles.Count > 0 Then
            Squiggles.Clear()
            PB.Refresh()
        End If
    End Sub

    Private Sub PB_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PB.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left Then
            curSquiggle = New Squiggle(CurColor.BackColor, CurWidth.Value)
            Squiggles.Add(curSquiggle)
            lastPoint = New Point(e.X, e.Y)
        End If
    End Sub

    Private Sub PB_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PB.MouseMove
        If e.Button = Windows.Forms.MouseButtons.Left Then
            If Not (curSquiggle Is Nothing) Then
                Dim curPoint As New Point(e.X, e.Y)
                curSquiggle.GP.AddLine(lastPoint, curPoint)
                lastPoint = curPoint
                PB.Refresh()
            End If
        End If
    End Sub

    Private Sub PB_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PB.MouseUp
        If e.Button = Windows.Forms.MouseButtons.Left Then
            If Not (curSquiggle Is Nothing) Then
                curSquiggle = Nothing
                lastPoint = Nothing
                PB.Refresh()
            End If
        End If
    End Sub

    Private Sub PB_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PB.Paint
        PaintSquiggles(e.Graphics)
    End Sub

    Private Sub PaintSquiggles(ByVal g As Graphics)
        Dim P As Pen
        For Each squig As Squiggle In Squiggles
            P = New Pen(squig.C, squig.W)
            g.DrawPath(P, squig.GP)
            P.Dispose()
        Next
    End Sub

End Class
0
 
LVL 3

Author Comment

by:rbradberry
Comment Utility
Thank you so much, this works perfectly.  You seriously helped me through a rediculous deadline.

-Russ
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

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 …
1.0 - Introduction Converting Visual Basic 6.0 (VB6) to Visual Basic 2008+ (VB.NET). If ever there was a subject full of murkiness and bad decisions, it is this one!   The first problem seems to be that people considering this task of converting…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

743 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

17 Experts available now in Live!

Get 1:1 Help Now