{ Rebuilds the tree for all expanded nodes from the node corresponding to
AStartDir (or from root if AStartDir is empty) to react on changes in the
file system. Collapsed nodes will be updated anyway when they are expanded. }
procedure TCustomShellTreeView.UpdateView(AStartDir: String = '');
procedure RecordNodeState(const ANode: TTreeNode; const AExpandedPaths: TStringList);
var
currentNode: TTreeNode;
firstChild: TTreeNode;
begin
currentNode := ANode;
while currentNode <> nil do
begin
if currentNode.Expanded then
begin
AExpandedPaths.Add(GetPathFromNode(currentNode));
firstChild := currentNode.GetFirstChild();
if firstChild <> nil then
RecordNodeState(firstChild, AExpandedPaths);
end;
currentNode := currentNode.GetNextSibling();
end;
end;
procedure RestoreNodeState(const ANode: TTreeNode; const ARefresh: boolean;
const AExpandedPaths: TStringList);
var
currentNode: TTreeNode;
firstChild: TTreeNode;
begin
currentNode := ANode;
while currentNode <> nil do
begin
if AExpandedPaths.IndexOf(GetPathFromNode(currentNode)) >= 0 then
begin
currentNode.Expanded := True;
if ARefresh then
Refresh(currentNode);
firstChild := currentNode.GetFirstChild();
if firstChild <> nil then
RestoreNodeState(firstChild, ARefresh, AExpandedPaths);
end
else
currentNode.Expanded := False;
currentNode := currentNode.GetNextSibling();
end;
end;
var
node: TTreeNode;
firstNode: TTreeNode;
startNode: TTreeNode;
topNodePath: String;
selectedPath: String;
selectedWasExpanded: Boolean = false;
expandedPaths: TStringList;
listviewRefreshNeeded: Boolean;
begin
if FUpdateLock <> 0 then
exit;
expandedPaths := TStringList.Create;
Items.BeginUpdate;
try
topNodePath := ChompPathDelim(GetPathFromNode(TopItem));
selectedPath := GetPathFromNode(Selected);
if Assigned(Selected) then
selectedWasExpanded := Selected.Expanded;
firstNode := Items.GetFirstNode;
if AStartDir = '' then
begin
startNode := firstNode;
listViewRefreshNeeded := true;
end else
begin
startNode := Items.FindNodeWithTextPath(ChompPathDelim(AStartDir));
// Avoid starting at a non-existing folder
while not Exists(GetPathFromNode(startNode)) and (startNode <> firstNode) do
startNode := startNode.Parent;
// Find out whether the StartDir is in the subtree ending at the selected
// node. In this case the listview must be refreshed, too.
node := Selected;
while (node <> startNode) and (node <> nil) do
node := node.Parent;
listviewRefreshNeeded := (node <> nil);
end;
RecordNodeState(startNode, expandedPaths);
RestoreNodeState(startNode, true, expandedPaths);
if Exists(selectedPath) then
begin
Path := selectedPath;
// Setting the path expands the selected node --> apply the stored state.
Selected.Expanded := selectedWasExpanded;
// Avoid selected node to scroll away.
TopItem := Items.FindNodeWithTextPath(topNodePath);
end;
// Force synchronization of associated ShellListView, but only if the
// refresh affects the selected tree node.
if Assigned(FShellListView) and listViewRefreshNeeded then
begin
inc(FUpdateLock);
try
FShellListView.UpdateView;
finally
dec(FUpdateLock);
end;
end;
finally
Items.EndUpdate;
expandedPaths.Free;
end;
end;