【问题标题】:How to synchronize the scrolling of 2 TTreeviews?如何同步2个TTreeviews的滚动?
【发布时间】:2012-05-09 23:05:03
【问题描述】:

我有 2 个 TTreeviews。他们两个都有相同数量的项目。 我希望能够同步他们的滚动条...如果我移动其中一个,其他的也会移动...

对于水平,它按我的预期工作...... 对于垂直,如果我使用滚动条的箭头,它会起作用,但如果我拖动拇指或如果我使用鼠标滚轮,它就不起作用...

这是我为说明我的问题而编写的示例:

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.

【问题讨论】:

    标签: delphi treeview scrollbar synchronized


    【解决方案1】:

    首先,一个有趣的测试:取消选中项目选项中的“启用运行时主题”,您将看到两个树视图将同步滚动。这向我们展示了树视图控件的默认窗口过程在不同版本的 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;
    

    【讨论】:

    • 在我的测试过程中,我想删除 VCL 主题,但我没有尝试没有运行时主题...顺便说一句,我已经成功测试了你的代码并且答案被明确接受,因为它确实是什么我一直在寻找...
    • @sertac-akyuz :事实上,WM_MOUSEWHEEL 仍然无法按预期工作......因为另一个 Trreview 没有重点,我不确定转发 TMessage 是否足以完成我的工作真的想要……有什么想法吗?
    • 好的,我用这个来管理鼠标滚轮:procedure TForm1.FormMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean); begin tv1.Perform(WM_VSCROLL, 1, 0); Handled := True; end; procedure TForm1.FormMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean); begin tv1.Perform(WM_VSCROLL, 0, 0); Handled := True; end;
    • @Whiler - 不同的鼠标驱动程序可能会以不同的方式处理滚动。你的代码在这里用我的鼠标开箱即用,所以我可能无法提出任何建议。很高兴你已经解决了。 :)
    • 对驱动程序 Sertac 来说没问题。谢谢。 (现在,我用键盘滚动)
    【解决方案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.
    

    【讨论】:

    • 谢谢...我忘了放回运行时主题 :(((但至少,它适用于鼠标滚轮...)
    • 好的,然后合并它们全部。 :)
    猜你喜欢
    • 2016-05-16
    • 2018-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    相关资源
    最近更新 更多