Copying the canvas graphics to the clipboard?

Hi all,

I’m not that familiar with the canvas etc yet so let me see if I get this right:

with Xojo you are supposed to do all your drawing in the canvas.Paint event, and no longer use a separate picture as a buffer which you then draw to the canvas (whole or in part).

Now I have a canvas, do all the drawing in the Paint event, and I would like to copy the graphics to the clipboard.

Note that the canvas might only show part of the graphics, but I need to copy all of the graphics.

How would I go about this?

TiA

Markus

Create a picture with all the contents and put that on the clipboard.

  • Karen

[quote=119734:@Markus Winter]Now I have a canvas, do all the drawing in the Paint event, and I would like to copy the graphics to the clipboard.

Note that the canvas might only show part of the graphics, but I need to copy all of the graphics.

How would I go about this?[/quote]

See http://documentation.xojo.com/index.php/Clipboard to place a picture to the clipboard.

Sample :

Dim c As New Clipboard If ImageWell1.Image <> Nil Then c.Picture = ImageWell1.Image c.Close Else MsgBox("No picture is available!") End If

I don’t think that is strictly true. I often use external pictures to do masses of drawing off screen and then copy the picture into my canvas. This has big speed implications over doing it all in the paint event.

One example is a grid. My grid is drawn in a Picture property. I only redraw the grid in the picture property if the main canvas has changed width or height. Then in the paint event of the main canvas I copy the grid picture into the canvas graphics. The reason I do this is the grid drawing is sloooow. Loop through X and Y coordinates putting a grid point at each X,Y point can be slow for instance if you have a grid point every 5 pixels. Therefore do this once or when your canvas changes size and then just copy it in to your canvas on the paint event. Big speed increase doing it like this.

Same method if you say had 10 drawn objects on a canvas and then draw an 11th. The only change is the 11th so I have what I call backCanvas property which is a picture with the 10 objects on which is copied into the main canvas on paint event. I actually draw my 11th on the backCanvas and then copy this in on the paint event. If not you would be drawing all 11 objects on every paint event.

I hope that makes sense.

[quote=119739:@Mike Charlesworth]I don’t think that is strictly true. I often use external pictures to do masses of drawing off screen and then copy the picture into my canvas. This has big speed implications over doing it all in the paint event.

One example is a grid. My grid is drawn in a Picture property. I only redraw the grid in the picture property if the main canvas has changed width or height. Then in the paint event of the main canvas I copy the grid picture into the canvas graphics. The reason I do this is the grid drawing is sloooow. Loop through X and Y coordinates putting a grid point at each X,Y point can be slow for instance if you have a grid point every 5 pixels. Therefore do this once or when your canvas changes size and then just copy it in to your canvas on the paint event. Big speed increase doing it like this.

Same method if you say had 10 drawn objects on a canvas and then draw an 11th. The only change is the 11th so I have what I call backCanvas property which is a picture with the 10 objects on which is copied into the main canvas on paint event. I actually draw my 11th on the backCanvas and then copy this in on the paint event. If not you would be drawing all 11 objects on every paint event.

I hope that makes sense.[/quote]

You are right, Mike. I read too fast and did not notice the mistake. Markus is not the first one fooled by the somewhat unclear formulation of the Canvas Paint event in the LR. What should not be done is to address the canvas.graphics object outside of the Canvas Paint event.

But it is quite fine to draw into a picture.graphics and then DrawPicture it in Canvas.Paint.

Thanks all.

[quote]Markus is not the first one fooled by the somewhat unclear formulation of the Canvas Paint event in the LR. What should not be done is to address the canvas.graphics object outside of the Canvas Paint event.

But it is quite fine to draw into a picture.graphics and then DrawPicture it in Canvas.Paint.[/quote]

Thanks for clearing that up for me.

No… you are supposed to INITIATE all drawing from the PAINT event

SUB PAINT(g as graphics, areas())

my_custom_event(g)

END SUB

Is 100% legal, and acceptable, and supported… The key is the Paint Event supplies the graphic object

sub myMethod
     window1.canvas.graphics.drawline 10,10,100,100
end sub

THIS is not acceptable, as you are referencing the graphics object outside the paint event (as opposed to referencing one supplied by the paint event)

[quote=119785:@Dave S]No… you are supposed to INITIATE all drawing from the PAINT event

The key is the Paint Event supplies the graphic object[/quote]

So where / how do I get the graphics onto the clipboard then? As far as I can see at the end I have my drawing in the graphics object.

I have the feeling some really obvious info might be missing for me that everyone else seems to take as a given.

“me.” being your canvas

Dim p As New Picture(Me.Width, Me.Height, 32)
Me.DrawInto(p.Graphics, 0, 0)

Dim c As New Clipboard
  c.Picture = p
  c.Close
1 Like

better yet


dim p as picture ' do this at the method or window level to increase the scope

SUB Paint(g as graphics)
   
   dim gg as graphics
   p=new picture(g.width,g.height)
   gg=p.graphics
   //
  //  do all you current drawing... but replace G with GG
 //
g.drawpicture p,0,0
// at this point picture "P" has what your canvas is displaying.....
END SUB

Picture P is now kept up to date, and can be printed, put on clipboard, saved to a file at any time/place in your program

[quote=119791:@Markus Winter]So where / how do I get the graphics onto the clipboard then? As far as I can see at the end I have my drawing in the graphics object.

[/quote]
try rectcontrol.drawinto

This works :

[code]
p as picture // window property

//In Canvas1
Sub Open()
p = new picture(me.width,me.height)
me.backdrop = p
End Sub

//in a button
Sub Action()
Canvas1.backdrop.Graphics.ForeColor = &cFF453E00
Canvas1.backdrop.Graphics.FillRect(0,10,100,60)
Canvas1.invalidate
End Sub[/code]

It does not draw to the Canvas.Graphics (forbidden), but to Canvas.Backdrop.Graphics :wink: Which modifies directly p.

In the above example, copying to the clipboard is done as (modified from the LR example) :

Dim c As New Clipboard If Canvas1.Backdrop <> Nil Then c.Picture = Canvas1.Backdrop c.Close Else MsgBox("No picture is available!") End If

This method will copy the canvas as the user sees it…

Function GetPicture(rc As RectControl) As Picture dim p As new Picture(rc.Width,rc.Height) rc.drawinto(p.Graphics,0,0) Return p End Function

use it like

dim p as picture=GetPicture(theCanvas)

Then you can stick p into the clipboard like Michel showed

The best method, imo, is to move all your drawing code out of the paint event and into a method that can be called from the paint event, passing the “g as graphics” object, and from a button, passing a picture’s graphics object.

The best alternative is to draw everything to a buffer picture, which you then drawpicture in the paint event. In both cases, all the logic is removed from the paint event, which keeps everything in sync.

RectControl.DrawInto has had some serious issues. It may be ok now, but I don’t trust it anymore. Test the heck out of it if you use it.

Thanks all, works beautifully now. The final method (which incorporates most of the advice here) for future reference:

add the properties pic as picture, picWidth as integer, picHeight as integer to the window

set up the picWidth and picHeight as you to your requirements
(in my case the width is the length of the item I want to show times the zoom factor plus some padding left and right)

Add a method DrawToPicture to the window

[code]dim p as New Picture( picWidth + 20, picHeight, 32)
pic = p
dim g as Graphics = pic.Graphics

// don’t forget to set the scrollbars for the new values

HScrollbar.Maximum = picWidth - canvas1.Width + 20
VScrollBar.Maximum = picHeight -canvas1.Height + 20

g.draw Whatever …[/code]

In the canvas paint event simple do

[code]g.ForeColor = &cFFFFFF
g.FillRect( 0, 0, g.Width - 1, g.Height - 1 )
// otherwise if the picture is smaller than the canvas
//then part of the canvas has a grey instead of a white background

If pic <> Nil Then
g.DrawPicture(pic, HScrollBar.Value * -1, VScrollbar.Value * -1)
End If[/code]

The code in the scrollbar ValueChanged event is simply

canProteinPrint.Invalidate

To copy the WHOLE graphics and not just the part shown in the canvas I use

dim c as new Clipboard c.Picture = pic c.Close

Hope I got all the relevant bits :slight_smile: