2015-04-22 155 views
4

我正在使用Zarko Gajic's Store More (Custom) Data Into The Tree Node Of A Tree View爲每個節點項目添加附加字符串,但是我發現如果我的應用程序長時間處於空閒狀態,那麼存儲在自定義樹節點中的值將消失。如何防止TTreeView的自定義樹節點數據丟失?

這是我的自定義樹節點看上去就像我離開的時候昨日

Before going home

這是它看起來像今天早上(注意fMyProperty值現在是空的)

Back the next day

我已確認我的電腦從不休眠或睡眠,但系統在鎖定1分鐘後確實會關閉顯示器以節能。但是,計算機需要閒置一段時間才能發生此問題。它在夜間空閒時最爲明顯,但如果只閒置30分鐘,則不太可能發生。

我能想到的唯一可導致這種情況的原因是操作系統將應用程序內存交換到磁盤,並且當您重新激活計算機時,它將被交換回內存。正如你所看到的,FItemId正在改變,所以它似乎是「重建」Treeview,從而失去了與自定義Tree節點的關聯。

我已經用非常簡單的應用程序轉載了這個問題,代碼如下。

我知道我可以使用另一種方法來存儲額外的數據,通過使用Treenode中的數據字段,但它能夠做到這一點很好,因爲我不必擔心釋放額外的內存塊當節點被刪除時。

我該怎麼做才能防止這種數據丟失的發生?

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版本也會出現此問題。

回答

6

這是因爲VCL窗口重新創建而發生的。 VCL設計意味着在某些情況下,需要重新創建實現窗體和控件的窗口。通常情況下,這種情況發生在狀態更改無法應用於已存在的窗口時。所以窗戶被重新創建。發生這種情況時,控件會嘗試保存其狀態,然後將其恢復。

對於樹視圖,節點本身被銷燬並重新創建。這意味着在參考節點時需要格外小心。一旦窗口重新創建,該節點引用無效。

您可以通過調用樹視圖控件的保護RecreateWnd方法來強制執行此操作。您需要受保護的成員訪問權限才能執行此操作。但只要您撥打RecreateWnd,您就可以觀察到您的自定義節點的屬性已被清除。事實上,爲您的節點類添加重寫的構造函數和析構函數,並觀察它們在重新創建期間被調用。

你在這裏很難受。據我所知,存儲並恢復節點狀態的樹視圖重新創建代碼沒有任何鉤子。您的自定義節點將被銷燬,並將被重新創建。我沒有看到在這個過程中堅持你的節點狀態的直接方式。Data屬性被持久化,因此可以使用它來確保正確重新創建自定義節點。但是一旦你走上這條路,定製節點會提供什麼好處?

我的建議是,您使用Data作爲您的自定義數據,並避免使用自定義節點類型。

據我所知,此問題呈現自定義節點類型功能接近無用。

+0

謝謝你的解釋:)我將使用數據功能。 – KeyszerS

+1

可以使用自定義節點類型作爲Data的類型安全包裝。另一方面,這也可以通過TTreeNode上的本地類助手完成。 –

+0

這兩個選項都非常好@Uwe,謝謝 –