Search code examples
delphidatagridtreeviewvcltreenode

Find node of Treeview using data of gridrow


First of all, I have a database: It can be either file or folder/map with these items (more columns are in the table, but I doubt they are required for this)

id | name | parent_id
----------------------------
1  | A    | nil
2  | A1   | 1
3  | A2   | 1
4  | A21  | 2
5  | B    | nil
6  | C    | nil
7  | C1   | 6

I have succeeded in making the tree by using a recursive query and function. Adding a node requires the parameters of (aParent, aString, aPointer):

Treeview.Items.AddObject(nil, qry.FieldByName('name').AsString, Pointer(qry.FieldByName('id').AsInteger))
Treeview.Items.AddChildObject(aParent, qry.FieldByName('name').AsString, Pointer(qry.FieldByName('id').AsInteger))

This all works fine (of course the actual code is larger, but my question is the following. I have a datagrid in use which shows my files and folders. Opening a folder/map in my datagrid or treeview refreshes my datagrid and shows the correct items within. However when I change folder i want my treeview to change and select the correct node as well.

All I have found is that I can select nodes by index, but I dont know the index of the node I want to go to. I do this at load so it always starts at the first node:

tvProjectDocuments.FullCollapse;
tvProjectDocuments.Selected:=tvProjectDocuments.Items[0];
tvProjectDocuments.Items[0].Selected:=true;

Now my question, I am able to use the pointer of my node to get the correct data in my datagrid, but is it possible to find a node by pointer(id)?

UPDATE: I managed to find the node by now

function TForm.FindNode(id: integer): TTreeNode;
var
  i: integer;
  found: boolean;
begin
  Result:=nil;
  i:=0;
  found:=false;
  while (i<Treeview.Items.Count-1) and not found do
  begin
    if Integer(Treeview.Items[i].Data)=id then
    begin
      result:=Treeview.Items[i];
      found:=True;
    end;
    inc(i);
  end;
end;

procedure TForm.tvProjectDocumentsChange(Sender: TObject; Node: TTreeNode);
begin
  if FFolderId = 0 then
  begin
    FFolderId:=Integer(Node.Data);

    //some code to get my grid filled data. works
    grid.Refresh;
    FFolderId:=0
  end;
end;

procedure TForm.GridCellDblClick(Sender: TObject; ACol, ARow: Integer);
begin
  FFolderId:=<CustomComponent>.FieldByName('proj_document_id').AsInteger;
  Treeview.Selected:=(FindNode(FFolderId));

  //other stuff happens
  FFolderId:=0;
end;

When it comes to the last Treenode, i get this access violation while debugging: FFolderId:=Node.Data in the Watch list it nil.

Afterwards it does load my datagrid, but it does not select the node corresponding to what should be. With all other nodes if i open a folder from datagrid, it does select and show the proper node.

I have prevented the error for now by using the FFolderId. But it still doesnt select my last node. Any info would be appreciated.


Solution

  • Selecting a node via its object can be achieved like this:

    function TForm10.GetNodeFromObject(const pNodes: TTreeNodes;
      pObject: TObject): TTreeNode;
    var
      i: Integer;
    begin
      for i := 0 to pNodes.Count - 1 do
      begin
        Result := pNodes.Item[ i ];
        if Result.Data = pObject then
        begin
          exit;
        end;
      end;
      // else
      Result := nil;
    end;
    

    You would then use it something like this

    var
      iNode : TTreeNode;
    begin
      iNode := GetNodeFromObject( TreeView.Items, Pointer( qry.FieldByName('id').AsInteger));
      if assigned( iNode ) then
      begin
        iNode.Selected := TRUE;
      end;
    end;
    

    in your case. Of course the function is more general than that.