2014-05-08 62 views
0

我有一個TLMDDockPanel部件作爲父的框架,在框架上有一個TTreeView控件:樹視圖項目的對象引用在斷開連接後發生更改?

unit devices; 
... 
    Tmaster = class(TObject) 
    ... 
    devTreeNode : ttreenode; 
    ... 
    end; 
... 
end. 

unit deviceTree; 
... 
    TfrmDevTree = class(TFrame) 
    JvTreeView1: TTreeView; 
    ... 
    end; 

procedure TfrmDevTree.GetSlavesOnSelectedClick(Sender: TObject); 
var 
    Node: TTreeNode; 
begin 
    ... 
    Node := self.JvTreeView1.Selected; 
    ... 
end; 
... 
end. 

unit mainForm; 
... 
TfrmMain = class(TForm) 
... 
    LMDDockSite1: TLMDDockSite; 
    LMDDockPanel_DevTree: TLMDDockPanel; 
... 
var 
    frmDevTree : TfrmDevTree; 
... 
procedure TfrmMain.FormCreate(Sender: TObject); 
begin 
    ... 
    frmDevTree := TfrmDevTree.Create(self); 
    frmDevTree.Parent := LMDDockPanel_DevTree; 
    ... 
end; 
... 
end. 

在應用程序開始,我填「數據」字段JvTreeView1的所有節點:

master := Tmaster.create; 
Node.Data := master; 
master.devtreenode := node; //I also save the treenode that is representing the master in JvTreeView1 into a master field. 

默認情況下,LMDDockPanel_DevTree停靠面板停靠在停靠點的左側,停靠面板坐在那裏時沒有任何問題,但是在取消停靠後,該目標。 treenodes的引用正在改變,所以存儲在主引擎(master.devtreenode)中的引用不再有效。 有人可以解釋爲什麼treenode引用會改變嗎?如何避免這種情況?每次我停靠/取消停靠面板時,是否應刷新存儲在主人中的所有參考?

謝謝。

+1

爲什麼你在'TMaster'類中存儲對TreeNode的引用?沒有理由這樣做,而且你將你的對象綁定到你的UI上,這是糟糕的設計。 –

+0

那麼這些引用對於TMaster的內部工作並不重要,但你可能是對的。 – grinner

回答

2

它發生的原因是因爲停靠/取消停頓會破壞並重新創建TreeView的HWND,而HWND會破壞並重新創建其節點對象。 TreeView旨在在此娛樂過程中自動緩存和恢復TTreeNode.Data值,但它對TMaster.DevTreeNode一無所知。因此,您需要檢測節點何時被重新創建,以便您可以使用新的TTreeNode指針手動更新其值DevTreeNode值。

一個TreeView有OnAdditionOnDeletion事件,人們認爲這將是理想的任務。但是,在HWND娛樂期間,它們不方便地被觸發!

所以,你有兩個選擇:

  1. 子類TreeView的WindowProc財產搭上娛樂消息。

    private 
        { Private declarations } 
        DefTreeViewWndProc: TWndMethod; 
        procedure TreeViewWndProc(var Message: TMessage); 
    

    procedure TfrmDevTree.FormCreate(Sender: TObject); 
    begin 
        DefTreeViewWndProc := JvTreeView1.WindowProc; 
        JvTreeView1.WindowProc := TreeViewWndProc; 
    end; 
    
    procedure UpdateMasterDevNode(Node: TTreeNode; Destroying: Boolean); 
    var 
        Child: TTreeNode; 
    begin 
        if Node.Data <> nil then 
        begin 
        if Destroying then 
         TMaster(Node.Data).DevTreeNode := nil 
        else 
         TMaster(Node.Data).DevTreeNode := Node; 
        end; 
        Child := Node.getFirstChild; 
        while Child <> nil do 
        begin 
        UpdateMasterDevNode(Child, Destroying); 
        Child := Child.getNextSibling; 
        end; 
    end; 
    
    procedure UpdateMasterDevNodes(Nodes: TTreeNodes; Destroying: Boolean); 
    var 
        Node: TTreeNode; 
    begin 
        Node := Nodes.GetFirstNode; 
        while Node <> nil do 
        begin 
        UpdateMasterDevNode(Node, Destroying); 
        Node := Node.getNextSibling; 
        end; 
    end; 
    
    procedure TfrmDevTree.TreeViewWndProc(var Message: TMessage); 
    const 
        WM_UPDATEMASTERDEVNODES = WM_APP + 1; 
    begin 
        if Message.Msg = CM_RECREATEWND then 
        UpdateMasterDevNodes(JvTreeView1.Items, True); 
    
        DefTreeViewWndProc(Message); 
    
        if Message.Msg = WM_CREATE then 
        begin 
        // the cached nodes have not been recreated yet, so delay the DevTreeNode updates 
        PostMessage(TreeView1.Handle, WM_UPDATEMASTERDEVNODES, 0, 0) 
        end 
        else if Message.Msg = WM_UPDATEMASTERDEVNODES then 
        UpdateMasterDevNodes(JvTreeView1.Items, False); 
    end; 
    
  2. 使用攔截器類重寫虛擬CreateWnd()DestroyWnd()方法。

    type 
        TJvTreeView = class(JVCL.ListsAndTrees.Trees.TJvTreeView) 
        protected 
        procedure CreateWnd; override; 
        procedure DestroyWnd; override; 
    end; 
    
    TfrmDevTree = class(TForm) 
        JvTreeView1: TJvTreeView; 
        ... 
    end; 
    

    procedure UpdateMasterDevNode(Node: TTreeNode; Destroying: Boolean); 
    var 
        Child: TTreeNode; 
    begin 
        if Node.Data <> nil then 
        begin 
        if Destroying then 
         TMaster(Node.Data).DevTreeNode := nil 
        else 
         TMaster(Node.Data).DevTreeNode := Node; 
        end; 
        Child := Node.getFirstChild; 
        while Child <> nil do 
        begin 
        UpdateMasterDevNode(Child, Destroying); 
        Child := Child.getNextSibling; 
        end; 
    end; 
    
    procedure UpdateMasterDevNodes(Nodes: TTreeNodes; Destroying: Boolean); 
    var 
        Node: TTreeNode; 
    begin 
        Node := Nodes.GetFirstNode; 
        while Node <> nil do 
        begin 
        UpdateMasterDevNode(Node, Destroying); 
        Node := Node.getNextSibling; 
        end; 
    end; 
    
    procedure TJvTreeView.CreateWnd; 
    begin 
        inherited; 
        UpdateMasterDevNodes(Items, False); 
    end; 
    
    procedure TTreeView.DestroyWnd; 
    begin 
        if csRecreating in ControlState then 
        UpdateMasterDevNodes(Items, True); 
        inherited; 
    end; 
    

無論哪種方式,確保其使用TMaster.DevTreeNode檢查零首先使用TTreeNode之前的任何代碼。

+0

太好了,謝謝。 – grinner

相關問題