Delphi FMX.Treeview函数TTreeViewContent.GetLastVisibleObjectIndex中出错

Delphi FMX.Treeview函数TTreeViewContent.GetLastVisibleObjectIndex中出错,delphi,firemonkey,Delphi,Firemonkey,我正在将VCL应用程序迁移到Firemonkey。它具有两个显示文件夹树的TTreeview控件。在主窗体的OnCreate事件处理程序中,从.Ini文件读取文件夹的路径,并设置TreeView。在主窗体出现在屏幕上之前的某个时刻,FMX.TreeView TTreeViewContent.GetLastVisibleObjectIndex函数中出现异常,即: function TTreeViewContent.GetLastVisibleObjectIndex: Integer; var

我正在将VCL应用程序迁移到Firemonkey。它具有两个显示文件夹树的TTreeview控件。在主窗体的OnCreate事件处理程序中,从.Ini文件读取文件夹的路径,并设置TreeView。在主窗体出现在屏幕上之前的某个时刻,FMX.TreeView TTreeViewContent.GetLastVisibleObjectIndex函数中出现异常,即:

function TTreeViewContent.GetLastVisibleObjectIndex: Integer;
var
  Item: TTreeViewItem;
begin
  if (FTreeView.FGlobalList.Count > 0) and (FTreeView.FLastVisibleItem < FTreeView.FGlobalList.Count) then
  begin
    Item := FTreeView.FGlobalList[FTreeView.FLastVisibleItem];
    {etc.}
  end
  else
    Result := ControlsCount;
end;
因为数组索引无效

此代码似乎与确定哪些treeview项目在treeview控件的滚动窗口中可见有关。由于错误发生在表单显示之前,因此我尝试在更新treview期间使treeview不可见,如下代码所示:

procedure TFormMain.UpdateTreeview(var Folder: TFolder; Treeview: TTreeview);
begin
  if Folder= FInputFolder then
    begin
      if not FTreeviewInputFolderValid then
        begin
          Treeview.Visible:= False;
          Treeview.BeginUpdate;
          FolderToTreeView(Folder, Treeview);
          //Treeview.InvalidateRect(Treeview.ContentRect);
          Treeview.ExpandAll;
          Treeview.EndUpdate;
          Treeview.Visible:= True;
          FTreeviewInputFolderValid:= True;
        end;
    end;
  {Ditto for FOutputFolder}
end;
如果在显示表单之前未设置treeview控件而运行程序,即未从.ini文件读取文件夹路径且未更新treeview控件,则不会发生错误

关于如何避免函数TTreeViewContent.GetLastVisibleObjectIndex中出现编码错误的建议

对于Tom,FolderToTreeview的代码为:

procedure TFormMain.FolderToTreeview(Folder: TFolder; Treeview: TTreeview);
var
  TreeviewOwner: TComponent;
  RootNode:  TTreeViewItemFolderCpt;

  procedure AddFolderChildCpts(ParentTreeNode: TTreeViewItemFolderCpt; Folder: TFolder);
  var
    i: integer;
    FolderCpt: TFolderCpt;
    FileCpt: TFileCpt;
    SubFolder: TFolder;
    ChildTreeNode: TTreeViewItemFolderCpt;
    Found: Boolean;
  begin
    {Add all cpts of folder to child nodes of ParentTreeNode}
    for i:= 0 to Folder.CptCount-1 do
      begin
        FolderCpt:= Folder.Cpts[i];
        ChildTreeNode:= TTreeViewItemFolderCpt.Create(ParentTreeNode);
        ParentTreeNode.AddObject(ChildTreeNode);
        ChildTreeNode.Parent:= ParentTreeNode;
        ChildTreeNode.FolderCpt:= FolderCpt;
        ChildTreeNode.OnPaint:= TreeViewItemPaint;
        if FolderCpt is TFileCpt then
          begin
            FileCpt:= FolderCpt as TFileCpt;
            ChildTreeNode.ImageIndex:= 1;
          end
        else if FolderCpt is TFolder then
          begin
            SubFolder:= FolderCpt as TFolder;
            ChildTreeNode.ImageIndex:= 0;
            {Recursively add subfolder:}
            AddFolderChildCpts(ChildTreeNode, SubFolder);
          end;
      end;
  end;

begin
  if not Folder.IsSorted then
    Folder.Sort(True);
  {Delete all existing nodes in tree:}
  Treeview.Clear;
  {Create a new root node and add to tree:}
  RootNode:= TTreeviewItemFolderCpt.Create(Treeview);
  Treeview.AddObject(RootNode);
  RootNode.Parent:=  Treeview;
  {Link folder object to root tree node:}
  RootNode.FolderCpt:= Folder;
  RootNode.ImageIndex:= 0;
  RootNode.OnPaint:= TreeViewItemPaint;
  {Now install child folder cpts:}
  AddFolderChildCpts(RootNode, Folder);
end;

TFolder、tfolderpt、TFileCpt是单独类层次结构的元素,用于在内存树结构中存储根文件夹下所有文件和文件夹的名称和元数据。类TFolder的根对象有一个方法Read(Path:string),该方法通过使用FindFirst和FindNext过程访问根目录下的文件和目录来生成其所有节点。在调用FolderToTreeview之前调用Folder.Read。由于这种额外的复杂性,我认为您无法运行FolderToTreeview方法

我已经找到了我发布的问题的解决方案。FolderToTreeview方法修改如下:

procedure TFormMain.FolderToTreeview(Folder: TFolder; Treeview: TTreeview;
                                           PaintEventHandler: TOnPaintEvent);
const
  COptionA= True;   {True if TreeviewItem.Clear used to destroy existing tree nodes}
  COptionB= False;  {True if PaintEventHandler assigned after tree nodes have been created}
var
  RootNode:  TTreeViewItemFolderCpt;
  i: integer;

  procedure AddFolderChildCpts(ParentTreeNode: TTreeViewItemFolderCpt;
                     Folder: TFolder; PaintEventHandler: TOnPaintEvent);
  var
    i: integer;
    FolderCpt: TFolderCpt;
    FileCpt: TFileCpt;
    SubFolder: TFolder;
    ChildTreeNode: TTreeViewItemFolderCpt;
  begin
    {Add all cpts of folder to child nodes of ParentTreeNode}
    for i:= 0 to Folder.CptCount-1 do
      begin
        FolderCpt:= Folder.Cpts[i];
        ChildTreeNode:= TTreeViewItemFolderCpt.Create(ParentTreeNode, FolderCpt);
        ParentTreeNode.AddObject(ChildTreeNode);
        ChildTreeNode.Parent:= ParentTreeNode;
        ChildTreeNode.OnPaint:= PaintEventHandler;
        if FolderCpt is TFileCpt then
          begin
            FileCpt:= FolderCpt as TFileCpt;
            ChildTreeNode.ImageIndex:= 1;
          end
        else if FolderCpt is TFolder then
          begin
            SubFolder:= FolderCpt as TFolder;
            ChildTreeNode.ImageIndex:= 0;
            {Recursively add subfolder:}
            AddFolderChildCpts(ChildTreeNode, SubFolder, PaintEventHandler);
          end;
      end;
  end;

  procedure SetSubNodesPaintEventHandler(ParentTreeNode: TTreeViewItem;
       PaintEventHandler: TOnPaintEvent);
  var
    i, j: integer;
    ChildTreeNodeI: TTreeViewItem;
  begin
    ParentTreeNode.OnPaint:= PaintEventHandler;
    {Assign PaintEventHandler to all  child nodes of TreeNode}
    for i:= 0 to ParentTreeNode.Count-1 do
      begin
        ChildTreeNodeI:= ParentTreeNode.Items[i];
        SetSubNodesPaintEventHandler(ChildTreeNodeI, PaintEventHandler);
      end;
  end;

begin
  if not Folder.IsSorted then
    Folder.Sort(True);
  Treeview.BeginUpdate;
  try
    {Delete all existing nodes in tree:}
    if COptionA then
      Treeview.Clear
    else
      for i:= Treeview.Count-1 downto 0 do
        Treeview.Items[i].Release;
    {Create a new root node and add to tree:}
    RootNode:= TTreeviewItemFolderCpt.Create(Treeview, Folder);
    Treeview.AddObject(RootNode);
    RootNode.Parent:=  Treeview;
    {Assign properties to root tree node:}
    RootNode.ImageIndex:= 0;
    {Now install child folder cpts:}
    if COptionB then
      {For testing purposes}
      AddFolderChildCpts(RootNode, Folder, nil)
    else
      AddFolderChildCpts(RootNode, Folder, PaintEventHandler);
    {Assign OnPaint event handler:}
    RootNode.OnPaint:= PaintEventHandler;
    if COptionB then
      {For testing purposes}
      SetSubNodesPaintEventHandler(RootNode, PaintEventHandler);
  finally
    Treeview.EndUpdate;
  end;
end;
最重要的更改是将Treeview节点更新代码包含在BeginUpdate中。。。EndUpdate括号

链接到TreeviewItem的FolderCpt现在在TTreeviewItemFolderCpt构造函数中分配:

TTreeViewItemFolderCpt = class(TTreeViewItem)
  protected
    FFolderCpt: TFolderCpt;
    procedure SetFolderCpt(const Value: TFolderCpt);
    function GetName: string;
    procedure SetName(Value: string);
  public
    constructor Create(Owner: TComponent; FolderCpt: TFolderCpt);
    property FolderCpt: TFolderCpt read FFolderCpt write SetFolderCpt;
    property Name: string read GetName write SetName;
  end;

constructor TTreeViewItemFolderCpt.Create(Owner: TComponent;
  FolderCpt: TFolderCpt);
begin
  inherited Create(Owner);
  FFolderCpt:= FolderCpt;
end;

您有哪个Delphi版本,以及如何将项目(在
FolderToTreeView()
中)添加到树状视图中?当我尝试在表单
OnCreate
中将项目添加到树视图时,我没有问题。我有Delphi社区版。使用ChildTreeNode添加项:=TTreeViewItemFolderCpt.Create(ParentTreeNode);ParentTreeNode.AddObject(ChildTreeNode);ChildTreeNode.Parent:=ParentTreeNode;那么,Delphi 10.3.2社区版?请添加“FolderToTreeView”代码,以便我可以在调试器中跟踪它。啊,你已经编辑了你的评论。很好,但也许您可以将其添加到您的问题帖子中。您在
FolderToTreeView()
中显示的几行代码不足以进行评估。太多的不确定性。
GetLastVisibleObjectIndex
中没有错误,因此错误必须出现在您的代码中,而您的代码没有显示到足够的程度以便进行分析。编辑您的问题并添加
FolderToTreeView()
语句中的完整代码“if(FTreeView.FGlobalList.Count>0)和(FTreeView.FLastVisibleItemTTreeViewItemFolderCpt = class(TTreeViewItem) protected FFolderCpt: TFolderCpt; procedure SetFolderCpt(const Value: TFolderCpt); function GetName: string; procedure SetName(Value: string); public constructor Create(Owner: TComponent; FolderCpt: TFolderCpt); property FolderCpt: TFolderCpt read FFolderCpt write SetFolderCpt; property Name: string read GetName write SetName; end; constructor TTreeViewItemFolderCpt.Create(Owner: TComponent; FolderCpt: TFolderCpt); begin inherited Create(Owner); FFolderCpt:= FolderCpt; end;