panel.drawtobitmap not working if rectangle startpoint is not 0, 0

Category: visual studio vb

Question

Breeze1953 on Sat, 13 Sep 2014 04:27:57


I am using VB 2010.

I have a DoubleBuffered panel (pnlMap) whose parent is another panel (pnlFrame).

The DoubleBuffered panel was created by the following code:-

Public Class DoubleBufferedPanel
    Inherits Panel

    Public Sub New()
        Me.DoubleBuffered = True
        SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint, True)
        UpdateStyles()
    End Sub
End Class

pnlMap contains many dynamically produced controls of either LineShapes, RectangleShapes or Pictureboxes. pnlMap is also a ShapeContainer for the Shape controls.

The function below is designed to return either the .left/.x or the .top/y coordinate of a control (LineShape or Picturebox)(MyObject) where it intersects with a particular LineShape (MyMovedLine). MyMovedLine has a bordercolor = color.hotpink. This works extra well if the Point1 = Point(0,0).

If I leave Point1 as (0,0) and the bitmap.size = pnlmap.size then after 5 attempts always get a 'Parameter Invalid Exception'. pnlMap is large so I expect that is because the bitmap is too large, although I don't know why it only happens on the 5th occasion.

If Point1 is anything other than (0,0), say (20,20), the created bitmap's starting point is at -20,-20 relevant to pnlMap and I get a grey or partially grey bitmap. I know this because I copied the bitmap to the clipboard and viewed it in MS Paint.

Ideally I would like to solve my (0,0) problem because I would like the bitmap (MyBMP)  to be created from a smaller portion anywhere in pnlMap. This is just about a showstopper for me.

Hoping you can help me. Code is:-

Function GetCoordOnHotPinkLine(MyMovedLine As LineShape, intStaticXCoord As Integer, intStaticYCoord As Integer, MyObject As Object) As Integer
        Dim intCoord As Integer = -1
        Dim MyColour As Color
        Dim Point1 As New System.Drawing.Point(0, 0)
        Dim Size1 As New System.Drawing.Size(GetLargest(MyMovedLine.X1, MyMovedLine.X2) + 10, GetLargest(MyMovedLine.Y1, MyMovedLine.Y2) + 10)
        Dim MyRectangle As New System.Drawing.Rectangle(Point1, Size1)
        Dim MyBMP As New Bitmap(MyRectangle.Width, MyRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)

        MyMovedLine.BorderColor = Color.HotPink 'change moved lineshape bordercolor from black to hotpink and do not want it in the bitmap
        MyObject.visible = False 'MyObject visible=false because sometimes MyObject is above MyMovedLine
        'MyObject will either be a LineShape or a Picturebox

        pnlMap.DrawToBitmap(MyBMP, MyRectangle) 'create the bitmap
        Clipboard.SetImage(MyBMP) 'for testing purposes only

        MyObject.visible = True 'restore MyObject.visible = true

        'the remainder works OK if the bitmap is correct
        If intStaticXCoord >= 0 Then
            For y = 1 To MyBMP.Height - 1
                MyColour = MyBMP.GetPixel(intStaticXCoord, y)
                If MyColour.ToArgb = Color.HotPink.ToArgb Then
                    intCoord = y + MyRectangle.Top
                    Exit For
                End If
            Next
        End If

        If intStaticYCoord >= 0 Then
            For x = 1 To MyBMP.Width - 1
                MyColour = MyBMP.GetPixel(x, intStaticYCoord)
                If MyColour.ToArgb = Color.HotPink.ToArgb Then
                    intCoord = x + MyRectangle.Left
                    Exit For
                End If
            Next
        End If

        MyBMP.Dispose()
        MyBMP = Nothing
        MyRectangle = Nothing

        MyMovedLine.BorderColor = Color.Black

        Return intCoord
    End Function


Replies

Mr. Monkeyboy on Sat, 13 Sep 2014 14:50:16


I didn't really read all your code. But I believe a method that will work is to draw the entire panel to one bitmap.

Creat a new Bitmap for the size of the drawing that you want to get from the original Bitmap should be.

Use graphics with the new Bitmap to draw just what you want from the old Bitmap into the new Bitmap.

I didn't have any luck trying to alter the drawing rectangle or anything else when attempting to have the Panel draw to a Bitmap just certain areas of the Panel.

Option Strict On

Public Class Form1

    WithEvents Panel1 As New DoubleBufferedPanel

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Location = New Point(CInt((Screen.PrimaryScreen.WorkingArea.Width / 2) - (Me.Width / 2)), CInt((Screen.PrimaryScreen.WorkingArea.Height / 2) - (Me.Height / 2)))
        With Panel1
            .Left = 30
            .Top = 30
            .Width = Me.ClientRectangle.Width - 60
            .Height = Me.ClientRectangle.Height - 60
            .BackColor = Color.Aqua
            .BorderStyle = BorderStyle.Fixed3D
        End With
        AddHandler Panel1.Paint, AddressOf Panel1_Paint
        Me.Controls.Add(Panel1)
        Button1.Left = CInt((Me.ClientRectangle.Width / 2) - (Button1.Width / 2))
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim Bmp1 As New Bitmap(Panel1.Width, Panel1.Height)
        Dim Rect As New System.Drawing.Rectangle
        Rect.X = 0
        Rect.Y = 0
        Rect.Width = Panel1.Width
        Rect.Height = Panel1.Height
        Panel1.DrawToBitmap(Bmp1, Rect)
        Dim Bmp2 As New Bitmap(Panel1.Width - 50, Panel1.Height - 50)
        Dim g As Graphics = Graphics.FromImage(Bmp2)
        g.DrawImage(Bmp1, -50, -50, Bmp1.Width, Bmp1.Height)
        Bmp2.Save("C:\Users\John\Desktop\Panel1.Png", System.Drawing.Imaging.ImageFormat.Png)
        Bmp1.Dispose()
        g.Dispose()
        Bmp2.Dispose()
    End Sub

    Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs)
        e.Graphics.DrawString("Testing drawing on panel.", New Font("Book Antiqua", 24), Brushes.Black, 20, CInt((Panel1.Height / 2) - 40))
    End Sub

    Public Class DoubleBufferedPanel
        Inherits Panel

        Public Sub New()
            Me.DoubleBuffered = True
            SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint, True)
            UpdateStyles()
        End Sub
    End Class

End Class

Image of Form

Image of Bmp2 saved to file.

Breeze1953 on Mon, 15 Sep 2014 02:50:00


Thanks for your replay.

If I use this line I will get an Invalid Parameter Exception on the 5th occasion.

Panel1.DrawToBitmap(Bmp1, Rect)

Acamar on Mon, 15 Sep 2014 02:53:13


If I use this line I will get an Invalid Parameter Exception on the 5th occasion.
Panel1.DrawToBitmap(Bmp1, Rect)

You need to insert a breakpoint at that line so that you can examine the variables each time the line of code executes, and watch how they change, and what the problem is with those values at the 5th occurrence.

Breeze1953 on Mon, 15 Sep 2014 03:12:50


Thanks again. I tested this and still the sample taken by bmp2 is outside the bounds of my pnlMap.

Mr. Monkeyboy on Mon, 15 Sep 2014 03:59:43


Thanks again. I tested this and still the sample taken by bmp2 is outside the bounds of my pnlMap.

Maybe you could display the code that you are attempting to use now. If so please use the Insert Code Block function and copy/paste your code into that window. In an open post the button in the toolbar at the top of the post being edited is square and to the right of the letters HTML which is used to insert a code block at the location in the post your caret is in prior to selecting that button.

An image of your app would be nice also if you are allowed to display images in posts yet.

I just used the code I displayed 100 times in a row with no issue.


La vida loca


Breeze1953 on Mon, 15 Sep 2014 05:46:29


I have used your code but reverted to my own no that I can move forward.

I have many times inserted a breakpoint and examined the values. They are what they should be.

Sorry, I don't see any 'Insert Code Block Function' or the letters HTML.

I think there is some issue with DrawToBitmap or System.Drawing.Rectangle.

Cor Ligthert on Mon, 15 Sep 2014 06:47:32


This is a part of your code. 

  If intStaticYCoord >= 0 Then
            For x = 1 To MyBMP.Width - 1
                MyColour = MyBMP.GetPixel(x, intStaticYCoord)
                If MyColour.ToArgb = Color.HotPink.ToArgb Then
                    intCoord = x + MyRectangle.Left
                    Exit For

everything behind it is never reached as soon as this is reached. 

Mr. Monkeyboy on Mon, 15 Sep 2014 14:57:24


I have used your code but reverted to my own no that I can move forward.

I have many times inserted a breakpoint and examined the values. They are what they should be.

Sorry, I don't see any 'Insert Code Block Function' or the letters HTML.

I think there is some issue with DrawToBitmap or System.Drawing.Rectangle.

Really? LOL. See image below. Note the red rectangle around the letters HTML. And the button to the right of it for Insert Code Block.

Breeze1953 on Tue, 16 Sep 2014 11:15:39


 Function GetCoordOnHotPinkLine(MyMovedLine As LineShape, intStaticXCoord As Integer, intStaticYCoord As Integer, MyObject As Object) As Integer
        Dim intCoord As Integer = -1
        Dim MyColour As Color
        Dim Point1 As New System.Drawing.Point(0, 0)
        Dim Size1 As New System.Drawing.Size(GetLargest(MyMovedLine.X1, MyMovedLine.X2) + 10, GetLargest(MyMovedLine.Y1, MyMovedLine.Y2) + 10)
        Dim MyRectangle As New System.Drawing.Rectangle(Point1, Size1)
        Dim MyBMP As New Bitmap(MyRectangle.Width, MyRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)

        MyMovedLine.BorderColor = Color.HotPink 'change moved lineshape bordercolor from black to hotpink and do not want it in the bitmap
        MyMovedLine.BringToFront()

        MyObject.visible = False 'MyObject visible=false because sometimes MyObject is above MyMovedLine
        'MyObject will either be a LineShape or a Picturebox
        For Each MyHandle In HandleCollection
            MyHandle.visible = False
        Next

        pnlMap.DrawToBitmap(MyBMP, MyRectangle) 'create the bitmap
        'Clipboard.SetImage(MyBMP) 'for testing purposes only

        MyObject.visible = True 'restore MyObject.visible = true
        For Each MyHandle In HandleCollection
            MyHandle.visible = True
        Next

        'the remainder works OK if the bitmap is correct
        If intStaticXCoord >= 0 Then
            For y = 1 To MyBMP.Height - 1
                MyColour = MyBMP.GetPixel(intStaticXCoord, y)
                If MyColour.ToArgb = Color.HotPink.ToArgb Then
                    intCoord = y + MyRectangle.Top
                    Exit For
                End If
            Next
        End If

        If intStaticYCoord >= 0 Then
            For x = 1 To MyBMP.Width - 1
                MyColour = MyBMP.GetPixel(x, intStaticYCoord)
                If MyColour.ToArgb = Color.HotPink.ToArgb Then
                    intCoord = x + MyRectangle.Left
                    Exit For
                End If
            Next
        End If

        MyBMP.Dispose()
        MyBMP = Nothing
        MyRectangle = Nothing

        If GetCategory(MyMovedLine) = "Running line" Then MyMovedLine.BorderColor = Color.Black
        If GetCategory(MyMovedLine) = "Siding" Then MyMovedLine.BorderColor = Color.Brown

        Return intCoord
    End Function

See it now thanks - I'll eat my hat if that was there yesterday.

Code is attached.

If Point1 <> (0,0) the resulting bitmap's starting point is always (-x,-y).

I have changed Point1 to (10,10) and (20,20) as a test and it always starts the bitmap at (-10,-10) or (-20,-20).

I tried to submit a sample of the resulting bitmap but it wouldn't let me until my account was verified??

In pnlMap.DrawToBitmap(MyBMP,MyRectangle) I get an 'Invalid parameter exception' if the bitmap is large. This is why I don't always want Point1 to be (0,0).

Thanks

Acamar on Tue, 16 Sep 2014 12:06:38


If Point1 <> (0,0) the resulting bitmap's starting point is always (-x,-y).

That is the expected behviour.  The target rectangle defines the bounds of an area within the destination bitmap at which the control is drawn.  If you define your target rectangle as starting at (10,10) then  the control will be drawn within that defined bounds - that is the (0,0) point of the control will be at position (10,10) in the bitmap. Or, the drawing in the bitmap will commence at a point (-10,-10) relative to the top left corner of the control.

Breeze1953 on Wed, 17 Sep 2014 00:42:26


Ok thanks. Works just fine now. I had the concept totally wrong. Thanks a lot.

Mr. Monkeyboy on Thu, 18 Sep 2014 19:00:58


Ok thanks. Works just fine now. I had the concept totally wrong. Thanks a lot.

Then you should propose the post that answered your question as the answer to end this thread.

Rudedog2 on Thu, 18 Sep 2014 19:41:01


Drawing on UI controls is very slow compared to drawing to an Image object instance in memory, and then setting the object instance as the BackgroundImage property.  Makes for a lot fewer Paint events that have to be handled.

Rudedog   =8^D