I'm using Zarko Gajic's Store More (Custom) Data Into The Tree Node Of A Tree View to add additional strings for each node item, but I have found that if my application has been idle for a long time, the values which I have stored in the customized tree nodes disappear.
This is what my custom treenode looked like when I left yesterday
This is what it looks like this morning (note the fMyProperty value is now empty)
I have confirmed that my computer never hibernates nor sleeps, but the system does turn off the monitor for power saving after 1 minute of being locked. The computer however needs to be idle for some time for this problem to occur. It's most noticeable when it's been idle overnight, but less likely to occur if only idle for 30 minutes.
The only thing I can think of which can be causing this is that the operating system is swapping the applications memory to disk, and when you reactivate your computer, it's swapped back to memory. As you can see, the FItemId is changing, so it seems to be "rebuilding" the Treeview, and thus losing the association with the custom Tree node.
I have reproduced this issue with a very simple application, code of which is below.
I know I can use another method to store additional data, by using the Data field in the Treenode, but it would be nice to be able to do this since I don't have to worry about freeing additional blocks of memory when nodes are deleted.
What can I do to prevent this data loss from occurring?
unit Test04Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.StdCtrls;
type
TMyTreeNode = class(TTreeNode)
private
fMyProperty : string;
public
property MyProperty : string read fMyProperty write fMyProperty;
end;
TForm1 = class(TForm)
TreeView1: TTreeView;
StatusBar1: TStatusBar;
procedure FormCreate(Sender: TObject);
procedure TreeView1CreateNodeClass(Sender: TCustomTreeView; var NodeClass: TTreeNodeClass);
procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
private
fTreeView1_Selected: TMyTreeNode;
property TreeView1_Selected : TMyTreeNode read fTreeView1_Selected;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
tn : TTreeNode;
cnt : integer;
begin
//fill some items
TreeView1.Items.Clear;
for cnt := 0 to 9 do
begin
tn := TreeView1.Items.AddChild(nil, IntToStr(cnt));
//add default MyProperty values
TMyTreeNode(tn).MyProperty := 'this is node ' + IntToStr(cnt);
end;
end;
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
fTreeView1_Selected := TMyTreeNode(Node);
StatusBar1.Panels[0].Text := TreeView1_Selected.MyProperty;
end;
procedure TForm1.TreeView1CreateNodeClass(Sender: TCustomTreeView; var NodeClass: TTreeNodeClass);
begin
NodeClass := TMyTreeNode;
end;
end.
Using Delphi XE6 on Windows 2012 R2, Debug Win32 build, but this problem occurs also with Win64 build.
This is happening because of VCL window re-creation. The VCL design means that in some situations, the windows that implement forms and controls need to be re-created. Typically this happens when a state change is made that cannot be applied to a window that already exists. So the windows are re-created. When this happens the controls attempt to save away their state, and then restore it.
For tree views, the nodes themselves are destroyed and then re-created. Which means that you need to take extreme care when taking a reference to a node. As soon as window re-creation happens, that node reference is invalid.
You can force this to happen yourself by calling the protected RecreateWnd
method of the tree view control. You'll need the protected member access hack to do that. But as soon as you call RecreateWnd
you can observe that your custom node's properties are cleared. Indeed add overridden constructor and a destructor to your node class and observe them being called during re-creation.
You are pretty much stumped here. The tree view re-creation code that stores and then restores the node state, does not have any hooks, so far as I can see. Your custom nodes will be destroyed, and will be re-created. I see no straightforward way for you to persist the state of your nodes during that process. The Data
property is persisted so you could use that to ensure correct re-creation of your custom node. But once you go down that path, what benefit does the custom node offer?
My recommendation is that you use Data
for your custom data and avoid using custom node types.
So far as I can tell, this issue renders the custom node type feature close to useless.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With