2009. március 30., hétfő

How to print a TScrollBox that contains controls generated at runtime


Problem/Question/Abstract:

How to print a TScrollBox that contains controls generated at runtime

Answer:

If this is some kind of custom control you developed yourself teach it to print itself. In fact it may be able to do that already using the PaintTo method. The main problem here is scaling. If your control uses device units (pixels) as measures instead of some device-independent unit like mm or inches you will need to scale the printer.canvas before you pass it to a controls PaintTo method. Scaling the printer canvas to the screen resolution is pretty straightforward. Here is an older example that you can tailor to your needs.

Print all of a forms client area, even if parts are not visible. The form will clip the output to the visible area if you try to output it to a canvas using using the forms paintto method. But one can print the controls on it individually and that is not clipped:

procedure TForm1.Button1Click(Sender: TObject);
var
  c: TControl;
  i: Integer;
  topX, topY: Integer;
begin
  printer.begindoc;
  try
    { Scale printer to screen resolution. }
    SetMapMode(printer.canvas.handle, MM_ANISOTROPIC);
    SetWindowExtEx(printer.canvas.handle, GetDeviceCaps(canvas.handle, LOGPIXELSX),
      GetDeviceCaps(canvas.handle, LOGPIXELSY), nil);
    SetViewportExtEx(printer.canvas.handle, GetDeviceCaps(printer.canvas.handle,                LOGPIXELSX),
      GetDeviceCaps(printer.canvas.handle, LOGPIXELSY), nil);
    topX := 10;
    topY := 10;
    for i := 0 to controlcount - 1 do
    begin
      c := controls[i];
      if c is TWinControl then
        TWinControl(c).paintto(printer.canvas.handle, c.left + topX, c.top + topy);
    end;
  finally
    printer.enddoc;
  end;
end;

The problem here is that this only prints TWinControl descendents, if you have TLabels or TImages on the form they are not printed. The solution is to put everything on the form onto a single top level TPanel. This panel is *not* aligned to alClient, it has its left and top set to 0 and its width and height is such that all controls fit on it. The code above then prints this panel unclipped and the panel prints any non-TWinControls on it.

The usual caveats for PaintTo apply: not all controls will implement this method properly (a Windows limitation). Bitmaps on the form may not appear on the printer if the printer is not able to print device-dependent bitmaps for the screen. It may be advisable to first paint the form to a properly sized tBitmaps canvas (you can omit all the scaling stuff for that since the bitmap resolution is the same as the screens) and then print the bitmap as a device independent bitmap using StretchDIBits.

Nincsenek megjegyzések:

Megjegyzés küldése