You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 10.4 FMX TTreeView自定义节点引发参数越界异常如何解决?

错误原因

TTreeViewItem的AddObject/Children属性是专门用来存储子树节点的,控件内部计算子节点数量、布局、展开折叠逻辑时,会默认遍历Children里的所有对象当成TTreeViewItem类型处理。你把普通的TRectangle和TEdit加到这个列表里,控件遍历的时候类型不匹配、索引计算错误,就会抛出EArgumentOutOfRangeException异常。


方案1:修复你现有代码的崩溃问题

只需要调整自定义节点中控件的挂载逻辑,不要把编辑器相关控件加到节点的Children列表里,显示时动态挂载到TreeView上即可:

  1. 先修改自定义节点的构造和析构逻辑:
constructor TVLinkTreeViewItem.Create(AOwner: TComponent);
begin
  inherited;
  fData := '';
  // Owner设为当前节点,自己管理销毁
  fBackground := TRectangle.Create(Self);
  fBackground.Visible := false;
  fBackground.Stored := false;
  fEditor := TEdit.Create(Self);
  fEditor.Visible := false;
  fEditor.Align := TAlignLayout.Client;
  fEditor.Stored := false;
  fEditor.OnKeyDown := EditorKeyUp;
  self.OnDblClick := TreeViewItem1DblClick;
  fEditor.OnExit := EditorExit;
end;

destructor TVLinkTreeViewItem.Destroy;
begin
  // 主动销毁创建的控件
  fEditor.Free;
  fBackground.Free;
  inherited;
end;
  1. 修改双击触发编辑的逻辑,动态定位编辑器位置:
procedure TVLinkTreeViewItem.TreeViewItem1DblClick(Sender: TObject);
var
  NodePos: TPointF;
begin
  // 计算当前节点在TreeView上的坐标
  NodePos := Self.LocalToAbsolute(PointF(20, 0));
  NodePos := TreeView1.AbsoluteToLocal(NodePos);
  // 动态把编辑器挂载到TreeView上
  fBackground.Parent := TreeView1;
  fBackground.Visible := true;
  fBackground.Width := Self.Width - 20;
  fBackground.Height := Self.Height;
  fBackground.Position.X := NodePos.X;
  fBackground.Position.Y := NodePos.Y;
  fEditor.Parent := fBackground;
  fEditor.Enabled := true;
  fEditor.Visible := true;
  fEditor.Opacity := 1;
  fBackground.BringToFront;
  fEditor.BringToFront;
  fEditor.Text := Text;
  fEditor.SetFocus;
  fEditor.SelectAll;
end;

方案2:更合理的FMX树节点就地编辑实现

推荐使用全局共用编辑器的方案,不会破坏树节点的内部结构,性能更高,逻辑更简单:

  1. 直接在窗体上放一个TEdit,默认设为隐藏,不用自定义TTreeViewItem类
  2. 绑定TTreeView的双击事件:
procedure TForm1.TreeView1DblClick(Sender: TObject);
var
  Node: TTreeViewItem;
  NodePos: TPointF;
begin
  Node := TreeView1.Selected;
  if Node = nil then Exit;
  // 计算节点位置
  NodePos := Node.LocalToAbsolute(PointF(20, 0));
  NodePos := TreeView1.AbsoluteToLocal(NodePos);
  // 定位编辑器
  Edit1.Parent := TreeView1;
  Edit1.Position.X := NodePos.X;
  Edit1.Position.Y := NodePos.Y;
  Edit1.Width := Node.Width - 20;
  Edit1.Height := Node.Height;
  Edit1.Text := Node.Text;
  Edit1.Visible := True;
  Edit1.BringToFront;
  Edit1.SetFocus;
  Edit1.SelectAll;
  // 存储当前编辑的节点
  Edit1.Tag := NativeInt(Node);
end;
  1. 绑定编辑器的退出和键盘事件:
procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
var
  Node: TTreeViewItem;
begin
  if Key = vkReturn then
  begin
    Node := TTreeViewItem(Edit1.Tag);
    if Assigned(Node) then
      Node.Text := Edit1.Text;
    Edit1.Visible := False;
  end
  else if Key in [vkEscape, vkCancel] then
    Edit1.Visible := False;
end;

procedure TForm1.Edit1Exit(Sender: TObject);
begin
  Edit1.Visible := False;
end;

自定义数据可以直接存在TTreeViewItem的Tag、TagString属性里,也可以继承TTreeViewItem加自定义属性,只要不往Children里加非节点控件就不会出现越界异常。


内容的提问来源于stack exchange,提问作者Rick Hollerich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 12:12:03