我有 2个TTreeviews 。它们都具有相同数量的项目。 我希望能够同步他们的滚动条 ...如果我移动其中一个,另一个也移动......
对于横向,它按预期工作... 对于垂直方向,如果我使用滚动条的箭头,它会起作用,但如果我拖动拇指或者我使用鼠标滚轮 ... < / p>
以下是我为解释我的问题而编写的一个示例:
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;
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;
begin
for i := 0 to 10 do
begin
tv1.Items.AddChild(nil, DupeString('A', 20) + IntToStr(i));
tv2.Items.AddChild(nil, DupeString('B', 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)
or (Msg.Msg = WM_HSCROLL)
or (Msg.msg = WM_Mousewheel)) then
begin
// tv2.Perform(Msg.Msg, Msg.wparam, Msg.lparam);
originalTv2WindowProc(Msg);
end;
end;
procedure TForm1.Tv2WindowProc(var Msg: TMessage);
begin
originalTv2WindowProc(Msg);
if ((Msg.Msg = WM_VSCROLL)
or (Msg.Msg = WM_HSCROLL)
or (Msg.msg = WM_Mousewheel)) then
begin
// tv1.Perform(Msg.Msg, Msg.wparam, Msg.lparam);
originalTv1WindowProc(Msg);
end;
end;
end.
DFM:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 113
ClientWidth = 274
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object tv1: TTreeView
Left = 8
Top = 8
Width = 121
Height = 97
Indent = 19
TabOrder = 0
end
object tv2: TTreeView
Left = 144
Top = 8
Width = 121
Height = 97
Indent = 19
TabOrder = 1
end
end
我也试过从TTreeview创建一个子类,但没有成功(相同的行为)...... 我尝试使用TMemo,它按预期工作......
我错过了什么?
干杯,
W上。
答案 0 :(得分:10)
首先,一个有趣的测试:取消选中项目选项中的“启用运行时主题”,您将看到两个树视图将同步滚动。这向我们展示了在不同版本的comctl32.dll中以不同方式实现树视图控件的默认窗口过程。看来,在垂直滚动时,comctl32 v6中的实现特别不同。
无论如何,似乎只有垂直滚动,控件会查找拇指位置,然后相应地调整窗口内容。当您将WM_VSCROLL
路由到相邻的树视图时,它会看到它的拇指位置,并且当它没有被更改时,决定无所事事(我们只更改了我们拖动的那个的拇指位置)。
因此,要使其正常工作,请在发送WM_VSCROLL
之前调整树视图的拇指位置。 tv1的修改过程如下所示:
procedure TForm1.Tv1WindowProc(var Msg: TMessage);
begin
originalTv1WindowProc(Msg);
if Msg.Msg = WM_VSCROLL then begin
if Msg.WParamLo = SB_THUMBTRACK then
SetScrollPos(tv2.Handle, SB_VERT, Msg.WParamHi, False);
end;
if ((Msg.Msg = WM_VSCROLL)
or (Msg.Msg = WM_HSCROLL)
or (Msg.msg = WM_Mousewheel)) then
begin
// tv2.Perform(Msg.Msg, Msg.wparam, Msg.lparam);
originalTv2WindowProc(Msg);
end;
end;
答案 1 :(得分:2)
<强>更新强>
我从French forum获得ShaiLeTroll的另一个答案:
此解决方案完美运行..我总是同步:箭头,拇指,水平,垂直,鼠标滚轮!
以下是更新后的代码(混合使用两种解决方案:拇指和鼠标滚轮):
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.