无法 select 虚拟树视图中的根节点

Cannot select the root node in Virtual Tree View

我正在使用带有虚拟树视图的 Delphi XE3。

我的代码如下:

type
  TMyData = record
    Caption: string;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  RootNode: PVirtualNode;
  PData: ^TMyData;
begin
  RootNode := tvItems.AddChild(nil);
  PData := tvItems.GetNodeData(RootNode);
  PData^.Caption := 'This is a test node';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  tvItems.NodeDataSize := SizeOf(TMyData);
end;

procedure TForm1.tvItemsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  PData: ^TMyData;
begin
  if Assigned(Node) then
  begin
    PData := tvItems.GetNodeData(Node);

    if Assigned(PData) then
      Celltext := PData^.Caption;
  end;
end;

当我点击"Button1"时,根节点将被创建。但是,当我的鼠标点击节点文本时,它不会被 selected.

我的一些发现:

  1. 必须单击节点文本的开头才能 select 节点。如果单击节点文本的中间或末尾,则不会select编辑该节点。

  2. 如果我将 tvItemsGetText 更改为以下,则问题消失:

    procedure TForm1.tvItemsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
    var
      PData: ^TMyData;
    begin
      CellText := 'This is a test node';
    end;

我在tvItemsGetText中设置了一个断点,发现它会被调用多次。前几次,PData 将为 nil,这使得 CellText 为空。在最后一次调用时,PData 将生效并且 CellText 将设置为 'This is a test node'.

看来允许鼠标点击的范围和select节点是由节点的初始文本决定的。如果初始文本为空字符串,则必须在节点的最开始单击才能select它。

这是虚拟树视图的错误吗?

有几种方法可以通过用户数据初始化新节点。

1。使用 OnInitNode 事件:

procedure TForm5.Button1Click(Sender: TObject);
begin
  vt1.InsertNode(nil, amAddChildLast); // internal calls vt1InitNode
end;

procedure TForm5.vt1InitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
  var InitialStates: TVirtualNodeInitStates);
var
  PData: ^TMyData;
begin
  PData := Sender.GetNodeData(Node);
  PData^.Caption := 'This is a test node';
end;

2 使用 UserData 参数

变体 1. 动态数据

不要忘记删除 InitNode 事件并且不要设置 NodeDataSize 属性

type
  TMyData = record
    Caption: string;
  end;
  PMyData = ^TMyData;

procedure TForm5.Button1Click(Sender: TObject);
var
  p: PMyData;
begin
  New(p);
  p.Caption:='This is a test node'; 
  vt1.InsertNode(nil, amAddChildLast, p); // create node with initialized user data
  // by default VirtualTree use NodeDataSize = SizeOf(pointer), 
  // so there is no reason to use GetNodeDataSize event 
end;


procedure TForm5.vt1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 
  Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
var
  PData: PMyData;
begin
  if Assigned(Node) then
  begin
    PData := PMyData(Sender.GetNodeData(Node)^); // little modification
    // for correct access to dynamic node data

    if Assigned(PData) then
      CellText := PData.Caption;
  end;
end;

procedure TForm5.vt1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  p: PMyData;
begin
  p:=PMyData(Sender.GetNodeData(Node)^);
  Dispose(p); // as you allocate memory for user data - you should free it to avoid memory leaks
end;

变体 2.对象

向您的表单添加新的私有函数:

  private
    { Private declarations }
    function GetObjectByNode<T: class>(Node: PVirtualNode): T; 
    // do not forget to include System.Generics.Collections to `uses`

实现:

function TForm5.GetObjectByNode<T>(Node: PVirtualNode): T;
var
  NodeData: Pointer;
  tmpObject: TObject;
begin
  Result := nil;
  if not Assigned(Node) then
    exit;
  NodeData := vt1.GetNodeData(Node);
  if Assigned(NodeData) then
    tmpObject := TObject(NodeData^);

  if tmpObject is T then
    Result := T(tmpObject)
  else
    Result := nil;
end;

以及主要代码(几乎与变体 1 相同):

procedure TForm5.Button1Click(Sender: TObject);
var
  d: TMyData;
begin
  d := TMyData.Create;
  d.Caption := 'This is a test node';
  vt1.InsertNode(nil, amAddChildLast, d);
end;

procedure TForm5.vt1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  d: TMyData;
begin
  d := GetObjectByNode<TMyData>(Node);
  d.Free;
end;

procedure TForm5.vt1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; 
  Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
var
  d: TMyData;
begin
  d := GetObjectByNode<TMyData>(Node);
  if Assigned(d) then
    CellText := d.Caption;
end;