Log On
Embarcadero Home
Watch, Follow, &
Connect with Us
Share This
QualityCentral
Communities
Articles
Blogs
Resources
Downloads
Help
QualityCentral
Delphi-BCB
VCL
Win 32 Controls
TAnimate
TComboBoxEx
TCoolBar
TDateTimePicker
THeaderControl
THotKey
TImageList
TListView
TMonthCalendar
TPageControl
TPageScroller
TProgressBar
TRichEdit
TStatusBar
TTabControl
TToolBar
TTrackBar
TTreeView
TUpDown
You are not logged in.
Help
Print
Public Report
Report From:
Delphi-BCB/VCL/Win 32 Controls/TTreeView
[ Add a report in this area ]
Report #:
3455
Status:
Closed
TTreeNode loses HasChildren property when TreeView handle is recreated.
Project:
Delphi
Build #:
4.453
Version:
7.0
Submitted By:
Constantine Yannakopoulos
Report Type:
Basic functionality failure
Date Reported:
1/30/2003 9:54:10 AM
Severity:
Serious / Highly visible problem
Last Updated:
3/20/2012 2:24:39 AM
Platform:
All versions
Internal Tracking #:
193516
Resolution:
Fixed
(Resolution Comments)
Resolved in Build:
:
9.0.1761.24408
Duplicate of:
None
Voting and Rating
Overall Rating:
(8 Total Ratings)
4.75 out of 5
Total Votes:
2
Description
In several cases it is necessary to perform incremental fetching of a treeview's nodes, either because initializing the whole tree structure is costly or because there are circular references in the structure that would make full initialization enter infinite recursion. To do this one would set the property TTreeNode.HasChildren to True at the time the node is added and use the event handler OnExpanding to load the child nodes when the node is being expanded.
This technique works but if for some reason the treeview's window handle is recreated then all the nodes that have ther HasChildren property set to True but have not been expanded yet by the user (therefore do not have any child nodes) lose their [+] sign, The reson for this is that the value of the TTreeNode's property HasChildren is not stored in the internal stream when the control's handle is recreated.
Steps to Reproduce:
Start a new project, place a TTreeView on the main form and name it "Tree".
In the form's OnCreate event handler add the following code:
procedure TForm1.FormCreate(Sender: TObject);
var
Node: TTreeNode;
begin
Node := Tree.Items.Add(nil, 'Current User');
Node.HasChildren := True;
end;
that adds the tree's root node. Then in the treeview's OnExpanding event handler add the code:
procedure TForm1.TreeExpanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
var
SubKeys: TStringList;
I: Integer;
ChildNode: TTreeNode;
begin
if Node.HasChildren and (Node.Count = 0) then
begin
with TRegistry.Create do
try
if OpenKey(string(PChar(Node.Data)), False) then
begin
SubKeys := TStringList.Create;
try
GetKeyNames(SubKeys);
for I := 0 to SubKeys.Count - 1 do
begin
ChildNode := Tree.Items.AddChild(Node, SubKeys[I]);
ChildNode.Data := StrNew(PChar(string(PChar(Node.Data)) + PathDelim + SubKeys[I]));
ChildNode.HasChildren := True;
end;
Node.HasChildren := SubKeys.Count <> 0;
finally
SubKeys.Free;
end;
end;
finally
Free;
end;
end;
end;
As you can see the code reads the subkeys of the selected registry key node and adds the corresponding child nodes. Notice that every new node has its HasChildren property set to True.
The treeview works well (although it has a memory leak) and displays the registry keys under HKEY_CURRENT_USER.
Then add a button to the form and in its OnClick handler write the code:
procedure TForm1.Button1Click(Sender: TObject);
begin
Tree.Perform(CM_RECREATEWND, 0, 0);
end;
that will force the treview to recreate its handle. Press the button and notice that any nodes that were not expanded have lost their [+] sign, so now you cannot expand them, even if the underlying registry key has subkeys.
Workarounds
I am afraid this can only be fixed in ComCtrls.pas itself. To fix modify TTreeNode.WriteData like this:
procedure TTreeNode.WriteData(Stream: TStream; Info: PNodeInfo);
var
I, Size, L, ItemCount: Integer;
begin
L := Length(Text);
if L > 255 then L := 255;
Size := SizeOf(TNodeInfo) + L - 255;
Info^.Text := Text;
Info^.ImageIndex := ImageIndex;
Info^.SelectedIndex := SelectedIndex;
Info^.OverlayIndex := OverlayIndex;
Info^.StateIndex := StateIndex;
Info^.Data := Data;
ItemCount := Count;
// ------------------------------------------------------------------
if (ItemCount = 0) and HasChildren then
Info^.Count := -1
else
// ------------------------------------------------------------------
Info^.Count := ItemCount;
Stream.WriteBuffer(Size, SizeOf(Size));
Stream.WriteBuffer(Info^, Size);
for I := 0 to ItemCount - 1 do
Item[I].WriteData(Stream, Info);
end;
and TTreeNode.ReadData like this:
procedure TTreeNode.ReadData(Stream: TStream; Info: PNodeInfo);
var
I, Size, ItemCount: Integer;
LNode: TTreeNode;
begin
Owner.ClearCache;
Stream.ReadBuffer(Size, SizeOf(Size));
Stream.ReadBuffer(Info^, Size);
Text := Info^.Text;
ImageIndex := Info^.ImageIndex;
SelectedIndex := Info^.SelectedIndex;
StateIndex := Info^.StateIndex;
OverlayIndex := Info^.OverlayIndex;
Data := Info^.Data;
ItemCount := Info^.Count;
//---------------------------------------------------------
HasChildren := ItemCount <> 0;
if ItemCount > 0 then
//---------------------------------------------------------
for I := 0 to ItemCount - 1 do
begin
LNode := Owner.AddChild(Self, '');
LNode.ReadData(Stream, Info);
Owner.Owner.Added(LNode);
end;
end;
Notice that the change is in the implementation section only.
While we are at it, i think that the streaming of TreeNodes should be made protected-virtual, so that if somebody decides to override TTreeView.CreateNode and return a TTreeNode descendant with additional data-members has also the chance to store these data-members in the internal stream.
---------
I am afraid this can only be fixed in ComCtrls.pas itself. To fix modify TTreeNode.WriteData like this:
procedure TTreeNode.WriteData(Stream: TStream; Info: PNodeInfo);
var
I, Size, L, ItemCount: Integer;
begin
L := Length(Text);
if L > 255 then L := 255;
Size := SizeOf(TNodeInfo) + L - 255;
Info^.Text := Text;
Info^.ImageIndex := ImageIndex;
Info^.SelectedIndex := SelectedIndex;
Info^.OverlayIndex := OverlayIndex;
Info^.StateIndex := StateIndex;
Info^.Data := Data;
ItemCount := Count;
// ------------------------------------------------------------------
if (ItemCount = 0) and HasChildren then
Info^.Count := -1
else
// ------------------------------------------------------------------
Info^.Count := ItemCount;
Stream.WriteBuffer(Size, SizeOf(Size));
Stream.WriteBuffer(Info^, Size);
for I := 0 to ItemCount - 1 do
Item[I].WriteData(Stream, Info);
end;
and TTreeNode.ReadData like this:
procedure TTreeNode.ReadData(Stream: TStream; Info: PNodeInfo);
var
I, Size, ItemCount: Integer;
LNode: TTreeNode;
begin
Owner.ClearCache;
Stream.ReadBuffer(Size, SizeOf(Size));
Stream.ReadBuffer(Info^, Size);
Text := Info^.Text;
ImageIndex := Info^.ImageIndex;
SelectedIndex := Info^.SelectedIndex;
StateIndex := Info^.StateIndex;
OverlayIndex := Info^.OverlayIndex;
Data := Info^.Data;
ItemCount := Info^.Count;
//---------------------------------------------------------
HasChildren := ItemCount <> 0;
if ItemCount > 0 then
//---------------------------------------------------------
for I := 0 to ItemCount - 1 do
begin
LNode := Owner.AddChild(Self, '');
LNode.ReadData(Stream, Info);
Owner.Owner.Added(LNode);
end;
end;
Notice that the change is in the implementation section only.
While we are at it, i think that the streaming of TreeNodes should be made protected-virtual, so that if somebody decides to override TTreeView.CreateNode and return a TTreeNode descendant with additional data-members has also the chance to store these data-members in the internal stream.
Attachment
None
Comments
Sebastian Modersohn at 2/5/2003 3:55:44 PM
-
Very good report! You may add a reference to the most prominent TreeView where this technique (setting HasChildren) is used: the one in Windows Explorer!
ACTUALLY: The same is true (loosing state after window recreation) for "Expanded" as well! If you recreate a TreeView all nodes are collapsed...
View Your Reports
Search
Server Response from: ETNACODE01
Developer Tools
Blackfish SQL
C++Builder
Delphi
FireMonkey
Prism
InterBase
JBuilder
J Optimizer
HTML5 Builder
3rdRail & TurboRuby
Database Tools
Change Manager
DBArtisan
DB Optimizer
ER/Studio
Performance Center
Rapid SQL
Technical Articles
Tutorials
White Papers
Press Releases
Newsletters
Add Content (GetPublished)
Audio
Audio & Video
Video
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Tags
Technology Partners
Downloads
Free Trials
Registered User Downloads
Beta Programs
Add Content (GetPublished)
Articles
Blogs
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Member Services
About
Connect with Us