Delphi FMX TTreeView Argument out of range exception

400 Views Asked by At

Using Delphi 10.4. I am hoping someone can explain what I am doing wrong with my FMX TTreeView that is causing an EArgumentOutOfRangeException. I am trying to create a custom TTreeViewItem class that allows me to associate some data with each node, as well as provide an in-place editor to allowing changing the node text.

The code below is a stripped down version of what I am doing. The FMX form has a TTreeview and two buttons on it, with the form's Onshow set to FormShow and the buttons set to the two button events.

The TVLinkTreeViewItem is my custom TTreeViewItem where I add a background and edit component for my in-place editor, which is displayed when a node is double clicked.

When you run the code as is, the program will throw the exception when the logic gets to the TreeView1.EndUpdate call at the end of the FormShow routine. The exception is thrown in FMX.Controls in the TControl.EndUpdate procedure.

If you comment out the ExpandAll call, the exception is not thrown, but if you mess with the expanding and collapsing of the nodes and resizing of the form, sooner or later the exception gets thrown. I left the ExpandAll line in the code below, as I assume the exception is being caused by the same error.

From what I can tell, the problem appears to be how I am setting up the fBackground and fEditor. If I don't call the AddObject routine and not set the Parent properties, I get no exception.

So can anybody tell me what I am doing wrong? Or is there a better way to do an in-place editor for the FMX TTreeViewItems component?

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TreeView, FMX.Layouts, FMX.Controls.Presentation,
  FMX.MultiView, FMX.Edit, FMX.Objects, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Button1: TButton;
    Button2: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

type
  TVLinkTreeViewItem = class(TTreeViewItem)
  private
    fData: string;
    fEditor: TEdit;
    fBackground: TRectangle;
    procedure TreeViewItem1DblClick(Sender: TObject);
    procedure EditorExit(Sender: TObject);
    procedure EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
  public
    property Editor: TEdit read fEditor write fEditor;
    property Data: string read fData write fData;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TreeView1.ExpandAll;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  TreeView1.CollapseAll;
end;

procedure TForm1.FormShow(Sender: TObject);
var
  I, c, r, s: Integer;
  vNode1,
  vNode2,
  vNode3,
  vNode4: TVLinkTreeViewItem;
begin
  TreeView1.BeginUpdate;
  TreeView1.Clear;
  for I := 0 to 4 do
    begin
      vNode1 := TVLinkTreeViewItem.Create(TreeView1);
      vNode1.Text := 'Level 1 - '+ IntToStr(I);
      TreeView1.AddObject(vNode1);
      for c := 0 to 4 do
        begin
          vNode2 := TVLinkTreeViewItem.Create(vNode1);
          vNode2.Text := 'Level 2 - '+ IntToStr(c);
          vNode1.AddObject(vNode2);
          for r := 0 to 4 do
            begin
              vNode3 := TVLinkTreeViewItem.Create(vNode2);
              vNode3.Text := 'Level 3 - '+ IntToStr(r);
              vNode2.AddObject(vNode3);
//              for s := 0 to 4 do
//                begin
//                  vNode4 := TVLinkTreeViewItem.Create(vNode3);
//                  vNode4.Text := 'Level 4 - '+ IntToStr(s);
//                  vNode3.AddObject(vNode4);
//                end;
            end;
        end;
    end;
  //ExpandAll works when no parent is set for fBackGround and fEditor is not set in "TVLinkTreeViewItem.Create" below"
  //If the Parents are set below, ExpandAll/EndUpdate causes "Augument out of range" exception.
  TreeView1.ExpandAll;
  treeView1.EndUpdate;
end;

{ TVLinkTreeViewItem }

constructor TVLinkTreeViewItem.Create(AOwner: TComponent);
begin
  inherited;
  fData := '';
  fBackground := TRectangle.Create(AOwner);
  //When ExpandAll is not called in FormShow,
  //   Calling "AddObject" or setting parent, as shown below, make all the code work,
  //   but will get intermident "Augument out of range" exceptions when resizing form,
  //   or when expanding or collapsing nodes using the buttons.
  self.AddObject(fBackGround);
  //fBackGround.Parent := self;
  fBackGround.Visible := false;
  fEditor := TEdit.Create(AOwner);
  fBackGround.AddObject(fEditor);
  //fEditor.Parent := fBackGround;
  fEditor.Visible := false;
  fEditor.Align := TAlignLayout.Client;
  fEditor.OnKeyDown := EditorKeyUp;
  self.OnDblClick := TreeViewItem1DblClick;
  fEditor.OnExit := EditorExit;
end;

destructor TVLinkTreeViewItem.Destroy;
begin

  inherited;
end;

procedure TVLinkTreeViewItem.TreeViewItem1DblClick(Sender: TObject);
begin
  fBackGround.Visible := true;
  fBackGround.Width := self.Width - 20;
  fBackGround.Height := self.Height;
  fBackGround.Position.X := 20;
  fEditor.Enabled := true;
  fEditor.Visible := true;
  fEditor.Opacity := 1;
  fBackGround.BringToFront;
  fEditor.BringToFront;
  fEditor.Text := Text;
  fEditor.SetFocus;
  fEditor.SelectAll;
end;

procedure TVLinkTreeViewItem.EditorKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
  inherited;
  if Key = vkReturn then
    begin
      Text := fEditor.Text;
      fBackGround.Visible := false;
      fEditor.Enabled := false;
    end
  else if Key in [vkEscape, vkCancel, vkTab, vkHardwareBack] then
    begin
      fBackGround.Visible := false;
      fEditor.Enabled := false;
    end;
end;

procedure TVLinkTreeViewItem.EditorExit(Sender: TObject);
begin
  fBackGround.Visible := false;
  fEditor.Enabled := false;
  fEditor.Visible := false;
end;

end.

Here's the fmx content:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 480
  ClientWidth = 640
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnShow = FormShow
  DesignerMasterStyle = 0
  object TreeView1: TTreeView
    Align = Left
    Size.Width = 269.000000000000000000
    Size.Height = 480.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
    Viewport.Width = 265.000000000000000000
    Viewport.Height = 476.000000000000000000
  end
  object Button1: TButton
    Position.X = 356.000000000000000000
    Position.Y = 68.000000000000000000
    TabOrder = 2
    Text = 'Expand'
    OnClick = Button1Click
  end
  object Button2: TButton
    Position.X = 354.000000000000000000
    Position.Y = 102.000000000000000000
    TabOrder = 1
    Text = 'Collapse'
    OnClick = Button2Click
  end
end
0

There are 0 best solutions below