2

I want to take a screenshot of a RichTextBox using the following code.
The problem is it takes a screenshot of another part of the Form:

Dim memoryImage As Bitmap
Dim myGraphics As Graphics = Me.CreateGraphics()
Dim s As Size = RichTextBox2.Size
memoryImage = New Bitmap(s.Width, s.Height, myGraphics)

Dim memoryGraphics As Graphics = Graphics.FromImage(memoryImage)
memoryGraphics.CopyFromScreen(RichTextBox2.Bounds.X, RichTextBox2.Bounds.Y, 0, 0, s)
memoryImage.Save(audiooutputfolder & name & ".png")
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • Winforms or WPF? – Robert Harvey Sep 20 '21 at 14:11
  • 1
    You likely need to transform local coordinates to screen coordinates. – Arvo Sep 20 '21 at 14:11
  • Meaning, Bounds and Location [refer to the parent container](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.location?view=net-5.0) (in this case, probably your form.) Sounds as if you would need to figure out what the relative location of your form is on the screen, and then adjust the coordinates of your bounding box accordingly. – Robert Harvey Sep 20 '21 at 14:16
  • could you please correct my code using the line you suggested?. I'm a beginner and don't know how to correct it. – Mehdi Esmaeili Sep 21 '21 at 05:49

1 Answers1

1

Graphics.CopyFromScreen() requires that you specify screen coordinates.
You can transform local coordinates into screen coordinates using the Control.RectangleToScreen() and Control.PointToScreen() methods.
Other methods do the opposite, see the Docs.

To compute the client area of a Control in screen coordinates, you can use its RectangleToScreen() method and pass the value of the ClientRectangle property:

Dim clientRectToScreen = [Control].RectangleToScreen([Control].ClientRectangle)

To include the non-client area (e.g., the borders of a Control, including the Scrollbars, if any), you need the screen coordinates of its Bounds.
There are different ways to do this. A simple method is to ask the Parent of a Control to get them, passing to the Parent's RectangleToScreen() method the Bounds of a child Control.
If you want to print a Form, which is a Top-Level Control, so it has no Parent, just use its Bounds directly: these measures already express screen coordinates.

It's shown in the ControlToBitmap() method:

Private Function ControlToBitmap(ctrl As Control, clientAreaOnly As Boolean) As Bitmap
    If ctrl Is Nothing Then Return Nothing
    Dim rect As Rectangle

    If clientAreaOnly Then
        rect = ctrl.RectangleToScreen(ctrl.ClientRectangle)
    Else
        rect = If(ctrl.Parent Is Nothing, ctrl.Bounds, ctrl.Parent.RectangleToScreen(ctrl.Bounds))
    End If

    Dim img As New Bitmap(rect.Width, rect.Height)
    Using g As Graphics = Graphics.FromImage(img)
        g.CopyFromScreen(rect.Location, Point.Empty, img.Size)
    End Using
    Return img
End Function

To take a screenshot of a Control, call this method, passing the Control you want to print to a Bitmap and specify whether you just want its content (the client area) or you want to include the non-client area (for example, if the control to print is a Form, you want to include the Caption and borders).

  • Important: use Path.Combine() to build a path:

    Path.Combine(audiooutputfolder, $"{imageName}.png"
    

    if string interpolation is not available ($"{variable} other parts"), you can glue the file extension to the file name:

    Path.Combine(audiooutputfolder, imageName & ".png")
    
' Get the screenshot, client area only
Dim controlImage = ControlToBitmap(RichTextBox2, True)
' Save the image to the specified Path using the default PNG format
controlImage.Save(Path.Combine(audiooutputfolder, $"{imageName}.png"), ImageFormat.Png)
' [...] when done with the bitmap
controlImage.Dispose()

Side note:
If your app is not DpiAware, you may get wrong screen coordinates.
See these notes about this.

Jimi
  • 29,621
  • 8
  • 43
  • 61
  • thank you for your help. but again it doesn't choose the correct part of the form . I tried to use drawtobitmap but it onley saves the borders not the contents. – Mehdi Esmaeili Sep 21 '21 at 10:21
  • Using bmp = New Bitmap(RichTextBox2.Width, RichTextBox2.Height) RichTextBox2.DrawToBitmap(bmp, New Rectangle(0, 0, bmp.Width, bmp.Height)) bmp.Save(audiooutputfolder & name & ".png") End Using – Mehdi Esmaeili Sep 21 '21 at 10:27
  • 1
    As mentioned, if using this code doesn't have the expected result, it's because you have a non-DpiAware app and the current screen that contains the window(s) you're trying to print is high-dpi or scaled (or you changed the code or you didn't use it as posted here). If that's the case, read the notes at the bottom of this post and make your app DpiAware. – Jimi Sep 21 '21 at 18:08