[TreeCtrl]Drag and Drop

Ref:Drag and drop

這邊需使用到CImageList來進行,D&D的圖示繪製。
由於CTreeCtrl有提供接收D&D的訊息事件,TVN_BEGINDRAG
新增的方式為,選擇於類別檢視內的CTreeCtrl衍伸類別,之後選擇屬性視窗,點選訊息,之後新增TVN_GEGINDRAG就可以了。

這邊會需要用到幾個data member,所以先宣告好。
Header:
 .....  
     protected:  
         CImageList *m_pDragImage;  
         HTREEITEM m_hDragItem;  
         HTREEITEM m_hDropItem;  
         BOOL m_IsLDragging;  
 .....  

在建立好OnTvnBegindrag()後,就可以在裡面實作按下滑鼠左鍵的行為:
 void CDSTreeCtrl::OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)  
 {  
     LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);  
     // TODO: 在此加入控制項告知處理常式程式碼  
     *pResult = 0;  
   
     this->m_hDragItem = pNMTreeView->itemNew.hItem;  
     this->m_pDragImage = this->CreateDragImage(this->m_hDragItem);  
   
     if ( !this->m_pDragImage ) {  
         return; };  
   
     this->m_IsLDragging = TRUE;  
     this->m_pDragImage->BeginDrag(0, CPoint(-15, -15));        //0 = current used image. Because of this item just has one image.  
                                                             //Under cursor, (-15, -15).  
     POINT pt = pNMTreeView->ptDrag;  
     ClientToScreen(&pt);  
     this->m_pDragImage->DragEnter(NULL, pt);  
     this->SetCapture();  
 }  

此時,僅有設定拖曳的時候所要顯示的圖示,所以還要處理WM_MOUSEMOVE的事件。這個訊息一樣可以透過上述方式來新增。
 void CDSTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)  
 {  
     // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值  
     HTREEITEM hItem = NULL;  
     UINT uFlag = 0;  
   
     if ( this->m_IsLDragging )  
     {  
         POINT pt = point;  
         ClientToScreen(&pt);  
         CImageList::DragMove(pt);  
   
         if ( NULL != (hItem = this->HitTest(point, &uFlag)) )  
         {  
             CImageList::DragShowNolock(FALSE);  
             this->SelectDropTarget(hItem);  
             this->m_hDropItem = hItem;  
             CImageList::DragShowNolock(TRUE);  
         }    //End of if ( NULL != (hItem = this->HitTest(point, &flags)) )  
     }    //End of if ( this->m_IsLDragging )  
   
     CTreeCtrl::OnMouseMove(nFlags, point);  
 }  

最後就是當滑鼠左鍵釋放時,要把選擇的item移到新的位置去。這邊的作法目前僅適用於同一個Tree的item移動。
如有要移到其他位置,那就要配合SendMessage()來處理了。
 void CDSTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)  
 {  
     // TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值  
     if ( this->m_IsLDragging )  
     {  
         this->m_IsLDragging = FALSE;  
         CImageList::DragLeave(this);  
         CImageList::EndDrag();  
         ::ReleaseCapture();  
   
         delete this->m_pDragImage;    //MUST TO DO.  
   
         this->SelectDropTarget(NULL);    //Remove drop target highlighting  
   
         if ( this->m_hDragItem == this->m_hDropItem ) {  
             return; };  
   
         //If Drag item is an ancestor of drop item then return  
         HTREEITEM hItemParent = this->m_hDropItem;  
         while ( NULL != (hItemParent = this->GetParentItem(hItemParent)) )  
         {  
             if ( hItemParent == this->m_hDragItem ) {  
                 return; };  
         }    //End of while ( NULL != (hItemParent = this->GetParentItem(hItemParent)) )  
   
         this->Expand(this->m_hDropItem, TVE_EXPAND);  
   
         //HTREEITEM hItemNew = NULL;  
         //hItemNew = CopyBranch(this->m_hDragItem, this->m_hDropItem, TVI_LAST);  
             //REF:http://www.vckbase.com/english/code/treeview/copy_item.shtml.htm  
         //this->DeleteItem(this->m_hDragItem);  
         //this->SelectItem(hItemNew);  
   
     }    //End of if ( this->m_IsLDragging )  
   
     CTreeCtrl::OnLButtonUp(nFlags, point);  
 }  

NOTE:由於TreeCtrl收到的座標原點在TreeCtrl內,所以當LButtonUp時,所得到的座標資訊,是無法用在其他的物件內。 故必須重新抓新的滑鼠座標。
處理方式有以下兩種:
1.直接用GetCursorPos()並配合ScreenToClient()來取得被當作Drop目的地的座標。
     pLDragOnGLView->pTree->ClientToScreen(&pLDragOnGLView->ptMousePos);  
     this->m_glWindow.ScreenToClient(&pLDragOnGLView->ptMousePos);  

2.將CTreeCtrl提供的point先用ClientToScreen()轉成螢幕座標,再用被當作Drop目的物件的ScreenToClient()來取得實際的座標位置。
     CPoint pt;  
     ::GetCursorPos(&pt);  
     this->m_glWindow.ScreenToClient(&pt);  
     this->m_glWindow.OnLButtonUp(pLDragOnGLView->unMouseMovementFlags, pLDragOnGLView->ptMousePos);  
   

然後由於在OnTvnBeginDrag()時,有設定SetCapture(),所以LButtonUp的訊息會先進到CTreeCtrl內,因此如有要把拖曳的物件放到別的控制項的話,必須在另外呼叫控制項的LButtonUp。

No comments:

Post a Comment

Build docker image from multiple build contexts

Build docker image from multiple build contexts ...