无法在虚拟树视图中选择根节点



我正在使用带有虚拟树视图的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"时,将创建根节点。但是,当我的鼠标单击节点文本时,它不会被选中。

我的一些发现:

  1. 必须单击节点文本的开头才能选择节点。如果在节点文本的中间或末尾单击,则不会选择节点。

  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 将设置为"这是一个测试节点"。

似乎允许鼠标单击和选择节点的范围由节点的初始文本决定。如果初始文本为空字符串,则必须单击节点的最开头才能将其选中。

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

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

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;

最新更新