如何同步滚动2个不同高度的TVirtualStringTree控件?

时间:2015-12-27 22:56:28

标签: delphi scroll delphi-xe7 virtualtreeview

我有2个TVirtualStringTree(VST)控件,一个在另一个上面。在TSplitter之间。滚动第一个时,我使用OnScroll of VST1 / 2滚动另一个VST2 / 1:

enter image description here

    procedure TForm1.VST1Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
    begin
      VST2.OffsetY:=VST1.OffsetY;
    end;

    procedure TForm1.VST2Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
    begin
      VST1.OffsetY:=VST2.OffsetY;
    end;

使用滚动条向上和向下滚动,效果很好。但只有它们都是相同的大小。问题是当高度不同时,VST1滚动到最后并且VST2仍然有很多可用,反之亦然,取决于哪个更高/更小。

我尝试了多种OffsetY *高度百分比的组合...不同的计算但是即使高度不同也没有任何滚动同步。

例如,如果VST1.Height = 100且VST.Height = 200,则VST1上的每个滚动应滚动VST2 2 * OffsetY,以匹配它们并同时滚动到底部。嗯,这样做不太好。

它们都具有相同的NodeCount(在附加示例20中,但可以有1000个)。

问题:如何计算一个VST中的每个滚动应该滚动另一个以进行同步?或者是另一种比不同高度

同步滚动两个VST更简单的方法

这是.pas

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, VirtualTrees;

type
  TForm1 = class(TForm)
    VST1: TVirtualStringTree;
    VST2: TVirtualStringTree;
    Splitter1: TSplitter;
    procedure FormCreate(Sender: TObject);
    procedure VST1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    procedure VST2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    procedure VST1Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
    procedure VST2Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  VST1.RootNodeCount := 20;
  VST2.RootNodeCount := 20;
end;

procedure TForm1.VST1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
  CellText:=IntToStr(Node.Index+1);
end;

procedure TForm1.VST1Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
begin
  VST2.OffsetY:=VST1.OffsetY;
end;

procedure TForm1.VST2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
begin
  CellText:=IntToStr(Node.Index+1);
end;

procedure TForm1.VST2Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
begin
  VST1.OffsetY:=VST2.OffsetY;
end;

end.

这里是.dfm:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 337
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Splitter1: TSplitter
    Left = 0
    Top = 100
    Width = 635
    Height = 3
    Cursor = crVSplit
    Align = alTop
    ExplicitWidth = 237
  end
  object VST1: TVirtualStringTree
    Left = 0
    Top = 0
    Width = 635
    Height = 100
    Align = alTop
    Header.AutoSizeIndex = 0
    Header.Font.Charset = DEFAULT_CHARSET
    Header.Font.Color = clWindowText
    Header.Font.Height = -11
    Header.Font.Name = 'Tahoma'
    Header.Font.Style = []
    Header.MainColumn = -1
    TabOrder = 0
    OnGetText = VST1GetText
    OnScroll = VST1Scroll
    Columns = <>
  end
  object VST2: TVirtualStringTree
    Left = 0
    Top = 103
    Width = 635
    Height = 234
    Align = alClient
    Header.AutoSizeIndex = 0
    Header.Font.Charset = DEFAULT_CHARSET
    Header.Font.Color = clWindowText
    Header.Font.Height = -11
    Header.Font.Name = 'Tahoma'
    Header.Font.Style = []
    Header.MainColumn = -1
    TabOrder = 1
    OnGetText = VST2GetText
    OnScroll = VST2Scroll
    Columns = <>
  end
end

2 个答案:

答案 0 :(得分:2)

VST有一个受保护的属性RangeY,它包含整个滚动范围,是解决方案的关键。

因此,ClientHeight - RangeY = VST中的最大负数OffsetY

代码可能如下所示:

type
  TForm1 = class(TForm)   
  ...
  private
    FScrolling: boolean;
    procedure SyncScroll(Sender, Target: TBaseVirtualTree);
  end;

...

type
  TCustomVirtualStringTreeAccess = class(TCustomVirtualStringTree);

procedure TForm1.SyncScroll(Sender, Target: TBaseVirtualTree);
var
  SenderMaxOffsetY, TargetMaxOffsetY: Integer;
  DY: Extended;
begin
  if FScrolling then Exit; // Avoid reentrancy from Target
  SenderMaxOffsetY := Sender.ClientHeight - Integer(TCustomVirtualStringTreeAccess(Sender).RangeY);
  TargetMaxOffsetY := Target.ClientHeight - Integer(TCustomVirtualStringTreeAccess(Target).RangeY);
  if SenderMaxOffsetY = 0 then Exit;
  DY := Sender.OffsetY / SenderMaxOffsetY;
  FScrolling := True;
  try
    Target.OffsetY := Round(TargetMaxOffsetY * DY);
  finally
    FScrolling := False;
  end;
end;

procedure TForm1.VST1Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
begin
  SyncScroll(Sender, VST2);
end;

procedure TForm1.VST2Scroll(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer);
begin
  SyncScroll(Sender, VST1);
end;

答案 1 :(得分:1)

如何同步顶部节点而不是尝试将两个树视图保持在像素完美同步中?我看到VT有一个TopNode属性,所以我会尝试这样的东西:

  • 在VT的init之后保存树的顶部节点;
  • OnScroll事件中检查当前顶级节点是什么 - 如果它已更改,则:
    • 记住树的新顶级节点;
    • 通知另一棵必须更新它的TopNode;

既然你说两棵树都有相同数量的节点,我假设它们显示相同的数据,因此可以在两棵树中将节点识别为“相同”(它们代表相同的数据)。