My problem is with a custom control I am trying to develop and I cannot seem to figure out how to implement the scroll bars correctly. I will highlight in key points what I am trying to do to make the question easier to understand.
- The control will be a simple image viewer, the image will be drawn in the center of the control.
- The control derives from
TScrollingWinControl
. - I have a published property called
FImage
which is aTPicture
class, this allows loading a image into the control. - There will be no child controls added as I will be painting the
FImage
onto the control. - In the constructor I have written
AutoScroll := False;
- I have intercepted the
WM_SIZE
message and here I determine offsets for centeringFImage
to the middle of the control and also try to recalculate the scroll ranges. - Finally I override the Paint method to draw the centered
FImage
onto the control.
So far so good, an image can be loaded at design or runtime and is displayed in the center of the control. Now I cannot understand how to get the scrolling set up properly.
Here is the relevant code so far:
unit uImageViewer;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.Classes,
Vcl.Controls,
Vcl.Forms,
Vcl.Graphics;
type
TMyImageViewer = class(TScrollingWinControl)
private
FCanvas: TCanvas;
FImage: TPicture;
FOffsetX: Integer; // center position in control for FImage
FOffsetY: Integer; // center position in control for FImage
procedure SetImage(const Value: TPicture);
private
procedure CalculateOffsets; //recalculates the center for FImage
procedure CalculateScrollRanges;
protected
procedure Loaded; override;
procedure PaintControl;
procedure PaintWindow(DC: HDC); override;
procedure WMEraseBkGnd(var Message: TMessage); message WM_ERASEBKGND;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
procedure WMSize(var Message: TMessage); message WM_SIZE;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Canvas: TCanvas read FCanvas;
published
property Align;
property Color;
property Image: TPicture read FImage write SetImage;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Standard', [TMyImageViewer]);
end;
constructor TMyImageViewer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create;
TControlCanvas(FCanvas).Control:=Self;
FImage := TPicture.Create;
Self.AutoSize := False; //?
AutoScroll := False;
ControlStyle := ControlStyle + [csOpaque];
end;
destructor TMyImageViewer.Destroy;
begin
FCanvas.Free;
FImage.Free;
inherited Destroy;
end;
procedure TMyImageViewer.Loaded;
begin
inherited Loaded;
CalculateOffsets;
CalculateScrollRanges;
end;
procedure TMyImageViewer.PaintControl;
procedure DrawClientBackground; // paints the control color
begin
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(ClientRect);
end;
begin
// if not (csDesigning in ComponentState) then
// begin
DrawClientBackground;
// draw the FImage
if (FImage <> nil) and (FImage.Graphic <> nil) then
begin
Canvas.Draw(FOffsetX, FOffsetY, FImage.Graphic);
end;
// end;
end;
procedure TMyImageViewer.PaintWindow(DC: HDC);
begin
FCanvas.Handle := DC;
try
PaintControl;
finally
FCanvas.Handle := 0;
end;
end;
procedure TMyImageViewer.SetImage(const Value: TPicture);
begin
if Value <> FImage then
begin
FImage.Assign(Value);
CalculateOffsets;
CalculateScrollRanges;
Invalidate;
end;
end;
procedure TMyImageViewer.CalculateOffsets;
begin
// for centering FImage in the middle of the control
if FImage.Graphic <> nil then
begin
FOffsetX := (Width - FImage.Width) div 2;
FOffsetY := (Height - FImage.Height) div 2;
end;
end;
procedure TMyImageViewer.CalculateScrollRanges;
begin
HorzScrollBar.Range:= FOffsetX + FImage.Width + FOffsetX;
VertScrollBar.Range:= FOffsetY + FImage.Height + FOffsetY;
end;
procedure TMyImageViewer.WMEraseBkGnd(var Message: TMessage);
begin
Message.Result := 1;
end;
procedure TMyImageViewer.WMPaint(var Message: TWMPaint);
begin
PaintHandler(Message);
end;
procedure TMyImageViewer.WMSize(var Message: TMessage);
begin
inherited;
CalculateOffsets;
CalculateScrollRanges;
Invalidate;
end;
end.
I originally started writing this in Lazarus but would also like to use it in Delphi hence both tags have been added.
How exactly should the scrollbars be calculated? Bearing in mind there is no children or auto scrolling enabled so it must be manual calculations, I am simply drawing a image in the center of the control and need to know how to calculate the scrollbar ranges etc.
I have tried a few different things with no success and it just seems like I am now putting anything in and hoping for the best, so I really could do with some guidance here please.
EDIT
So having tried running the original code in Delphi has now made me realise how much more different Lazarus is, lots of things had to be changed to run under Delphi and even right now the scrollbars are disappearing.