Search code examples
delphivirtualtreeviewtvirtualstringtree

GetText from TVirtualStringTree is getting triggered more times


Please take a look to bellow code:

unit Unit2;

interface

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

type
  TTreeData = record
    Fields: array of String;
  end;
  PTreeData = ^TTreeData;

const
  szVirtualTree = SizeOf(TTreeData);

type
  TForm2 = class(TForm)
    tree: TVirtualStringTree;
    Button1: TButton;
    procedure treeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    procedure treeGetNodeDataSize(Sender: TBaseVirtualTree;
      var NodeDataSize: Integer);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
  LTreeData: PTreeData;
  Node: PVirtualNode;
  cols, rows, col, row: Integer;
begin
  cols:= 2;
  rows:= 2;

  Tree.Header.Columns.Clear;
  if cols > 0 then
    for col := 0 to cols - 1 do
      with Tree.Header.Columns.Add do
        begin
          Text:= 'H' + IntToStr(col);
          Width:= 80;
        end;

  Tree.Clear;
  Tree.BeginUpdate;

  if (cols> 0) and (rows > 0) then
    for row := 0 to rows - 1 do
      begin
        Node:= Tree.AddChild(nil,nil);
        Tree.ValidateNode(Node,False);

        LTreeData:= Tree.GetNodeData(Node);
        SetLength(LTreeData^.Fields, cols);

        if cols > 0 then
          for col := 0 to cols - 1 do
            LTreeData^.Fields[col]:= '[' + inttostr(col) + ',' + inttostr(row) + ']';
      end;

  Tree.EndUpdate;
end;

procedure TForm2.treeGetNodeDataSize(Sender: TBaseVirtualTree;
  var NodeDataSize: Integer);
begin
  NodeDataSize:= szVirtualTree;
end;

procedure TForm2.treeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  LTreeData: PTreeData;
begin
  LTreeData:= Sender.GetNodeData(Node);
  CellText:= LTreeData^.Fields[Column];
  showmessage(CellText);
end;

end.

After the button gets pressed the messages i get are:

[0,0] 
[0,0] 
[0,0] 
[0,0] 
[1,0]
[1,0]
[1,0]
[1,0] 
[0,1]
[0,1]
[0,1]
[0,1] 
[1,1]
[1,1]
[1,1]
[1,1]

Basically the GetText is triggered 4 times for each node and each column.

Why is not getting triggered only one time? Like this: [0,0] [1,0] [0,1] [1,1]

enter image description here


Solution

  • The control uses the so called virtual paradigm. The author Mike Lischke explains this like so:

    As the name already indicates, this control uses a different paradigm for tree management than other controls of this kind. It does not know anything about the data it manages (except its size), not even the captions of a node. Everything is retrieved from the application via events (or descendants via overridden methods).

    The control does not keep track of the node captions. Whenever it needs to know them it fires the OnGetText method. The control does not cache, does not remember the node captions. So if multiple parts of the control code depend on the caption then the event will be called multiple times for the same node.

    The behaviour that you observe is perfectly normal and entirely to be expected. The job of your OnGetText handler is to yield the node's text. Do not worry if it is asked the same question twice. Just make sure that you give the same answer each time!