Updated:
Another answer I received on the French forum from ShaiLeTroll :
This solution works fine .. I always synchronized: arrows, thumb, horizontal, vertical, mouse wheel!
Here is the updated code ( which mixes both solutions: for thumb and for mouse wheel ):
unit main; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StrUtils; type TForm1 = class(TForm) tv1: TTreeView; tv2: TTreeView; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } originalTv1WindowProc : TWndMethod; originalTv2WindowProc : TWndMethod; sender: TTreeView; procedure Tv1WindowProc (var Msg : TMessage); procedure Tv2WindowProc (var Msg : TMessage); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); var i: Integer; tn: TTreeNode; begin for i := 0 to 20 do begin tn := tv1.Items.AddChild(nil, DupeString('A', 20) + IntToStr(i)); tv1.Items.AddChild(tn, DupeString('C', 20) + IntToStr(i)); tv1.Items.AddChild(tn, DupeString('C', 20) + IntToStr(i)); tn := tv2.Items.AddChild(nil, DupeString('B', 20) + IntToStr(i)); tv2.Items.AddChild(tn, DupeString('D', 20) + IntToStr(i)); tv2.Items.AddChild(tn, DupeString('D', 20) + IntToStr(i)); end; originalTv1WindowProc := tv1.WindowProc; tv1.WindowProc := Tv1WindowProc; originalTv2WindowProc := tv2.WindowProc; tv2.WindowProc := Tv2WindowProc; end; procedure TForm1.FormDestroy(Sender: TObject); begin tv1.WindowProc := originalTv1WindowProc; tv2.WindowProc := originalTv2WindowProc; originalTv1WindowProc := nil; originalTv2WindowProc := nil; end; procedure TForm1.Tv1WindowProc(var Msg: TMessage); begin originalTv1WindowProc(Msg); if Msg.Msg = WM_VSCROLL then begin if Msg.WParamLo = SB_THUMBTRACK then begin SetScrollPos(tv2.Handle, SB_VERT, Msg.WParamHi, False); end; end; if (sender <> tv2) and ((Msg.Msg = WM_VSCROLL) or (Msg.Msg = WM_HSCROLL) or (Msg.Msg = WM_MOUSEWHEEL)) then begin sender := tv1; tv2.Perform(Msg.Msg, Msg.wparam, Msg.lparam); sender := nil; end; end; procedure TForm1.Tv2WindowProc(var Msg: TMessage); begin originalTv2WindowProc(Msg); if Msg.Msg = WM_VSCROLL then begin if Msg.WParamLo = SB_THUMBTRACK then begin SetScrollPos(tv1.Handle, SB_VERT, Msg.WParamHi, False); end; end; if (sender <> tv1) and ((Msg.Msg = WM_VSCROLL) or (Msg.Msg = WM_HSCROLL) or (Msg.Msg = WM_MOUSEWHEEL)) then begin sender := tv2; tv1.Perform(Msg.Msg, Msg.wparam, Msg.lparam); sender := nil; end; end; end.