The following example shows one way to subclass TScrollBox. It has rather oversimplified TWMxScroll() message procedures. However, they are sufficient to demonstrate the principle and yield smooth synchronised scrolling for me.
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, Forms, StdCtrls, LMessages;
type
TScrollExEvent = procedure(Sender: TObject; var ScrollPos: Integer) of object;
{ TScrollBoxEx }
TScrollBoxEx = class(TScrollBox)
strict private
FOnVScroll: TScrollExEvent;
FOnHScroll: TScrollExEvent;
protected
procedure WMHScroll(var Message : TLMHScroll); message LM_HScroll;
procedure WMVScroll(var Message : TLMVScroll); message LM_VScroll;
procedure DoHScroll(var aScrollPos: integer);
procedure DoVScroll(var aScrollPos: integer);
published
property OnVScroll: TScrollExEvent read FOnVScroll write FOnVScroll;
property OnHScroll: TScrollExEvent read FOnHScroll write FOnHScroll;
end;
{ TForm1 }
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
sb1, sb2: TScrollBoxEx;
mem1, mem2, mem3, mem4: TMemo;
procedure VScrollHandler(Sender: TObject; var ScrollPos: integer);
procedure HScrollHandler(Sender: TObject; var ScrollPos: integer);
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TScrollBoxEx }
procedure TScrollBoxEx.DoHScroll(var aScrollPos: integer);
begin
if Assigned(FOnHScroll) then
FOnHScroll(Self, aScrollPos);
end;
procedure TScrollBoxEx.DoVScroll(var aScrollPos: integer);
begin
if Assigned(FOnVScroll) then
FOnVScroll(Self, aScrollPos);
end;
procedure TScrollBoxEx.WMHScroll(var Message: TLMHScroll);
begin
HorzScrollBar.Position:=Message.Pos;
DoHScroll(Message.Pos);
end;
procedure TScrollBoxEx.WMVScroll(var Message: TLMVScroll);
begin
VertScrollbar.Position:=Message.Pos;
DoVScroll(Message.Pos);
end;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
sb1:=TScrollBoxEx.Create(Self);
sb1.Name:='sb1';
sb1.Left:=10;
sb1.Top:=10;
sb1.OnVScroll:=@VScrollHandler;
sb1.OnHScroll:=@HScrollHandler;
sb1.Parent:=Self;
mem1:=TMemo.Create(Self);
mem1.Parent:=sb1;
mem3:=TMemo.Create(Self);
mem3.Top:=100;
mem3.Left:=100;
mem3.Parent:=sb1;
sb2:=TScrollBoxEx.Create(Self);
sb2.Name:='sb2';
sb2.Left:=180;
sb2.Top:=10;
sb2.OnVScroll:=@VScrollHandler;
sb2.OnHScroll:=@HScrollHandler;
sb2.Parent:=Self;
mem2:=TMemo.Create(Self);
mem2.Parent:=sb2;
mem4:=TMemo.Create(Self);
mem4.Left:=100;
mem4.Top:=100;
mem4.Parent:=sb2;
end;
procedure TForm1.HScrollHandler(Sender: TObject; var ScrollPos: integer);
var
sndr, synchee: TScrollBoxEx;
begin
if (Sender is TScrollBoxEx) then
sndr:=TScrollBoxEx(Sender)
else
Exit;
if (sndr = sb1) then
synchee:=sb2
else synchee:=sb1;
synchee.HorzScrollBar.Position:=ScrollPos;
end;
procedure TForm1.VScrollHandler(Sender: TObject; var ScrollPos: integer);
var
sndr, synchee: TScrollBoxEx;
begin
if (Sender is TScrollBoxEx) then
sndr:=TScrollBoxEx(Sender)
else
Exit;
if (sndr = sb1) then
synchee:=sb2
else synchee:=sb1;
synchee.VertScrollBar.Position:=ScrollPos;
end;
end.