【问题标题】:How can I prevent custom Tree Node Data loss for a TTreeView?如何防止 TTreeView 的自定义树节点数据丢失?
【发布时间】:2015-04-24 03:18:22
【问题描述】:

我正在使用Zarko Gajic's Store More (Custom) Data Into The Tree Node Of A Tree View为每个节点项添加额外的字符串,但我发现如果我的应用程序长时间空闲,我存储在自定义树节点中的值就会消失。

这是我昨天离开时自定义树节点的样子

这就是今天早上的样子(注意 fMyProperty 值现在为空)

我已确认我的计算机从不休眠或休眠,但系统会在锁定 1 分钟后关闭显示器以省电。但是,计算机需要空闲一段时间才能发生此问题。整夜闲置时最明显,但仅闲置 30 分钟则不太可能发生。

我能想到的唯一可能导致此问题的原因是操作系统正在将应用程序内存交换到磁盘,并且当您重新激活计算机时,它会交换回内存。如您所见,FItemId 正在发生变化,因此它似乎正在“重建” Treeview,从而失去与自定义 Tree 节点的关联。

我用一个非常简单的应用程序重现了这个问题,代码如下。

我知道我可以使用另一种方法来存储额外的数据,即使用 Treenode 中的 Data 字段,但是能够这样做会很好,因为我不必担心释放额外的内存块节点被删除。

我可以做些什么来防止这种数据丢失的发生?

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.

在 Windows 2012 R2 上使用 Delphi XE6,调试 Win32 版本,但 Win64 版本也会出现此问题。

【问题讨论】:

    标签: delphi


    【解决方案1】:

    这是由于 VCL 窗口重新创建而发生的。 VCL 设计意味着在某些情况下,需要重新创建实现窗体和控件的窗口。通常情况下,当状态更改无法应用于已存在的窗口时,就会发生这种情况。所以窗口被重新创建。当这种情况发生时,控件会尝试保存它们的状态,然后恢复它。

    对于树视图,节点本身会被销毁,然后重新创建。这意味着在引用节点时需要格外小心。一旦重新创建窗口,该节点引用就无效。

    您可以通过调用树视图控件的受保护RecreateWnd 方法来强制执行此操作。你需要受保护的成员访问黑客来做到这一点。但是,只要您调用RecreateWnd,您就可以观察到您的自定义节点的属性已被清除。确实将重写的构造函数和析构函数添加到您的节点类并观察它们在重新创建期间被调用。

    你在这里几乎被难住了。据我所知,存储然后恢复节点状态的树视图重新创建代码没有任何钩子。您的自定义节点将被销毁,并将重新创建。在此过程中,我认为您没有直接的方法来保持节点的状态。 Data 属性是持久的,因此您可以使用它来确保正确重新创建自定义节点。但是,一旦您沿着这条路走下去,自定义节点会带来什么好处?

    我的建议是您对自定义数据使用Data,并避免使用自定义节点类型。

    据我所知,此问题使自定义节点类型功能几乎无用。

    【讨论】:

    • 感谢您的解释 :) 我将改用数据功能。
    • 可以使用自定义节点类型作为数据的类型安全包装器。另一方面,这也可以通过 TTreeNode 上的本地类助手来完成。
    • 另外,考虑使用 Virtual Treeview - 一种专为将数据与节点关联而设计的树。另外,它是一个非常好的控件。不过,它确实有一个陡峭的学习曲线。
    • 很好的答案。我想知道为什么我存储的 TTreeNodes 在一段时间后变得无效。现在我有一些工作要做。
    猜你喜欢
    • 2022-11-21
    • 2017-01-26
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 2012-10-19
    • 1970-01-01
    相关资源
    最近更新 更多