Recent

Author Topic: Synchronising two scrollboxes  (Read 4615 times)

hakelm

  • Full Member
  • ***
  • Posts: 153
Synchronising two scrollboxes
« on: May 26, 2015, 06:37:56 pm »
I have several scrollboxes, each containing an image. Now I want to synchronise the scrolling of the images using the scrollbars of just one of the scrollboxes. The scrollboxes do, however, have no onscroll events.
The only solution, I have found, is to use two extra tscrollbars and their onscroll events.
Does anyone have a better solution to this problem?
Thanks in advance for any tip.
H

Blaazen

  • Hero Member
  • *****
  • Posts: 3237
  • POKE 54296,15
    • Eye-Candy Controls
Re: Synchronising two scrollboxes
« Reply #1 on: May 26, 2015, 07:13:10 pm »
I also see there no suitable event. Maybe you should try to create a simple class inherited from TScrollBox with some published events. I didn't see code of TScrollBox  but I guess there are some methods where you can hook.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

balazsszekely

  • Guest
Re: Synchronising two scrollboxes
« Reply #2 on: May 26, 2015, 07:49:59 pm »
Quick(should I say stupid? :D) solution. Drop a TTimer to your form. Set Interval to 100.
Code: [Select]
var
  OldX, OldY: Integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  OldX := ScrollBox1.HorzScrollBar.Position;
  OldY := ScrollBox1.VertScrollBar.Position;
end;

procedure TForm1.tmScrollTimer(Sender: TObject);
begin
  if (OldX <> ScrollBox1.HorzScrollBar.Position) or
      (OldY <> ScrollBox1.VertScrollBar.Position) then
  begin
    OldX := ScrollBox1.HorzScrollBar.Position;
    OldY := ScrollBox1.VertScrollBar.Position;
    ScrollBox2.HorzScrollBar.Position := OldX;
    ScrollBox2.VertScrollBar.Position := OldY;
  end;
end;                     
This will synchronize ScrollBox2 with ScrollBox1.


Basile B.

  • Guest
Re: Synchronising two scrollboxes
« Reply #3 on: May 26, 2015, 07:56:41 pm »
Quick(should I say stupid? :D) solution. Drop a TTimer to your form.

no t stupid , a property binding engine with automatic update would also be based on a timer, a TIdleTimer more exactly. a timer is the only solution if the event is not implemented.

balazsszekely

  • Guest
Re: Synchronising two scrollboxes
« Reply #4 on: May 26, 2015, 08:02:43 pm »
Quote
no t stupid , a property binding engine with automatic update would also be based on a timer, a TIdleTimer more exactly. a timer is the only solution if the event is not implemented.
The OP should:
  1. Subclass TScrollBar and publish the appropriate event(s)
  2. Write a class helper
My solution is easy, but not the ideal one, that's for sure

hakelm

  • Full Member
  • ***
  • Posts: 153
Re: Synchronising two scrollboxes
« Reply #5 on: May 26, 2015, 08:56:05 pm »
Thanks all for your rapid and insightful inputs.
I have tried:
1)
procedure TForm1.scrollsync(Sender: TObject; var done: boolean);
begin
  scrollbox2.HorzScrollBar.Position:=scrollbox1.HorzScrollBar.Position;
  scrollbox2.VertScrollBar.Position:=scrollbox1.VertScrollBar.Position;
  done:=true;
end;
..........
  application.OnIdle:=@scrollsync;
...........

and

2)
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  scrollbox2.HorzScrollBar.Position:=scrollbox1.HorzScrollBar.Position;
  scrollbox2.VertScrollBar.Position:=scrollbox1.VertScrollBar.Position;
.....
end;
with a timer1.interval=10 ms

But neither of these gave me the smooth tracking I want so I will stick to the 2 extra scrollbars.
H
 

sfeinst

  • Full Member
  • ***
  • Posts: 230
Re: Synchronising two scrollboxes
« Reply #6 on: May 26, 2015, 09:52:01 pm »
I think the best option is to subclass and expose the events, but if you are really against it, you could keep track of the horiz and vert scrollbar positions of the main tscrollbox, and then in its onPaint event, check if it changed and update the others.

Guessing your code will get called more than you need, which is why subclassing is better, but might be worth trying as a quick fix.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Synchronising two scrollboxes
« Reply #7 on: May 26, 2015, 10:28:34 pm »
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.

Code: [Select]
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.


hakelm

  • Full Member
  • ***
  • Posts: 153
Re: Synchronising two scrollboxes
« Reply #8 on: May 26, 2015, 11:00:02 pm »
Thanks howardpc for a nice and easy to understand lesson in more advanced programming than I am used to.
Works without a glitch.
H

 

TinyPortal © 2005-2018