A la primera pregunta, un solución seria modificar la función PaintTo del TWinControl para que al dibujar no limite al tamaño del control y utilizar esta función para dibujar en un TBitmap y finalmente imprimirlo.
Esto seria un ejemplo:
Código Delphi
[-]
...
implementation
uses
Math;
type
TWC = class(TWinControl) end;
function GetPaintAllBmp(AControl: TWinControl): TBitmap;
procedure PaintAllTo(AControl: TWinControl; DC: HDC; X, Y: Integer);
procedure DrawThemeEdge(DC: HDC; var DrawRect: TRect);
var
Details: TThemedElementDetails;
Save: Integer;
begin
Save := SaveDC(DC);
try
with DrawRect do
ExcludeClipRect(DC, Left + 2, Top + 2, Right - 2, Bottom - 2);
Details := ThemeServices.GetElementDetails(teEditTextNormal);
ThemeServices.DrawElement(DC, Details, DrawRect);
finally
RestoreDC(DC, Save);
end;
InflateRect(DrawRect, -2, -2);
end;
var
w, h: Integer;
I, EdgeFlags, BorderFlags, SaveIndex: Integer;
R: TRect;
begin
w := AControl.ClientWidth;
h := AControl.ClientHeight;
for I := 0 to AControl.ControlCount - 1 do
begin
w := Max(w, AControl.Controls[i].Left + AControl.Controls[i].Width);
h := Max(h, AControl.Controls[i].Top + AControl.Controls[i].Height);
end;
SaveIndex := SaveDC(DC);
try
MoveWindowOrg(DC, X, Y);
IntersectClipRect(DC, 0, 0, w, h);
BorderFlags := 0;
EdgeFlags := 0;
if GetWindowLong(AControl.Handle, GWL_EXSTYLE) and WS_EX_CLIENTEDGE <> 0 then
begin
EdgeFlags := EDGE_SUNKEN;
BorderFlags := BF_RECT or BF_ADJUST
end else
if GetWindowLong(AControl.Handle, GWL_STYLE) and WS_BORDER <> 0 then
begin
EdgeFlags := BDR_OUTER;
BorderFlags := BF_RECT or BF_ADJUST or BF_MONO;
end;
if (EdgeFlags = EDGE_SUNKEN) and ThemeServices.ThemesEnabled and
not ((csDesigning in AControl.ComponentState) and UnthemedDesigner(AControl)) then
begin
SetRect(R, 0, 0, w, h);
if csNeedsBorderPaint in AControl.ControlStyle then
DrawThemeEdge(DC, R)
else
begin
AControl.ControlStyle := AControl.ControlStyle + [csNeedsBorderPaint];
AControl.ControlStyle := AControl.ControlStyle - [csNeedsBorderPaint];
end;
MoveWindowOrg(DC, R.Left, R.Top);
IntersectClipRect(DC, 0, 0, R.Right - R.Left, R.Bottom - R.Top);
end
else if BorderFlags <> 0 then
begin
SetRect(R, 0, 0, w, h);
MoveWindowOrg(DC, R.Left, R.Top);
IntersectClipRect(DC, 0, 0, R.Right - R.Left, R.Bottom - R.Top);
end;
AControl.Perform(WM_PAINT, DC, 0);
for I := 0 to AControl.ControlCount - 1 do
if AControl.Controls[i] is TWinControl then
with AControl.Controls[i] do
if Visible then
PaintAllTo(AControl.Controls[i] as TWinControl, DC, Left, Top);
finally
RestoreDC(DC, SaveIndex);
end;
end;
var
I: Integer;
w,h: Integer;
begin
w := AControl.ClientWidth;
h := AControl.ClientHeight;
for I := 0 to AControl.ControlCount - 1 do
begin
w := Max(w, AControl.Controls[i].Left + AControl.Controls[i].Width);
h := Max(h, AControl.Controls[i].Top + AControl.Controls[i].Height);
end;
Result := TBitmap.Create;
Result.Canvas.Brush.Color := TWC(AControl).Color;
Result.SetSize(w, h);
Result.Canvas.Lock;
try
PaintAllTo(AControl, Result.Canvas.Handle, 0, 0);
finally
Result.Canvas.Unlock;
end;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Picture.Assign(GetPaintAllBmp(Self));
end;
La función GetPaintAllBmp devuelve un Bitmap con todos los controles dibujados sin importar el tamaño del control principal.
A la segunda pregunta, dibujar el componente DBGrid entero es un problema diferente, ya que este componente no contiene subcomponentes sino que las filas son dibujadas, y solo unas filas son visibles dependiendo de la posición del scroll. A falta de otra solución, yo haría lo que tu mismo has comentado, de manera manual, campo a campo.
Saludos