2014년 10월 29일 수요일

CTreeCtrl drag and drop

퍼온글

MFC CTreectrl Item Drag & Drop

출처 : http://maluchi.cafe24.com/xe/?mid=MyProgrammingTips&page=6&listStyle=list&document_srl=15989



[ 트리컨트롤(Tree Control) ]







1. 대화상자에 트리컨트롤을 붙이고 옵션을 다음과 같이 수정하자.













   Edit labels: 트리컨트롤에서 에디트 기능을 사용할때.



   Show selection always: 선택된 아이템을 표시할때.



2. 맴버 변수를 m_ctrTree라고 만들자(Control형 하나밖에 없다).



3. 아이템 추가하기

   TVINSERTSTRUCT  TI;

   TI.hParent  = TVI_ROOT;        // TVI_ROOT, NULL

                         // HTREEITEM값을 사용하면 해당하는 아이템의 자식으로 아이템이 추가된다.

   TI.hInsertAfter = TVI_LAST;    // TVI_FIRST, TVI_LAST, TVI_SORT

   TI.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

   TI.item.iImage = 0;                // Tree가 선택안되었을때 표시될 아이콘

   TI.item.iSelectedImage = 1;   // Tree가 선택되었을때 표시될 아이콘

   TI.item.pszText = "root";



   HTREEITEM hTreeItem = m_ctrTree.InsertItem(&TI); // 추가된 아이템의 HTREEITEM이 리턴된다.



4. 아이템 확장하기.

   m_ctrTree.Expand(hTreeItem, TVE_EXPAND);



5. 아이템 선택시 선택된 아이템 알아보기

   + TVN_SELCHANGED메시지를 사용한다.

   NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

   HTREEITEM hTreeItem = pNMTreeView->itemNew.hItem;   // 이 값이 선택된 아이템의 핸들이다.



6. 아이템 문자열 가져오기

   CString str = m_ctrTree.GetItemText(hTreeItem);



7. 아이템 개수 알아내기

   int nCount = m_ctrTree.GetCount();



8. 아이템 제거하기

   m_ctrTree.DeleteItem(hTreeItem);   // 핸들 아래단의 아이템들도 모두 제거된다.



9. 현재 선택된 아이템 알아내기

   HTREEITEM hTreeItem = m_ctrTree.GetSelectedItem();



10. 위치로 아이템 찾기

   CPoint  p;

   GetCursorPos(&p);

   ::ScreenToClient(m_ctrTree.m_hWnd, &p);

   HTREEITEM hItem = m_ctrTree.HitTest(p);



11. 아이템 확장 축소 감지

   + TVN_ITEMEXPANDED메시지를 사용한다.

   NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;



   TVITEM  item;

   item.mask = TVIF_HANDLE;

   item.hItem = pNMTreeView->itemNew.hItem;

   m_ctrTree.GetItem(&item);            // 아이템 정보를 알아낸다.



   if(item.state & TVIS_EXPANDED)

   {

      // 확장

   }

   else

   {

      // 축소

   }



12. 아이템 아이콘 설정 변경

   m_ctrTree.SetItemImage(hTreeItem, 0, 1);



13. 아이템 에디트 입력중 포커스가 나갈때 입력중인 값 아이템에 적용하기

   + TVN_ENDLABELEDIT메시지를 사용한다.

   TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;



   CEdit *pEdit = m_ctrTree.GetEditControl();

   if(pEdit)

   {

      CString str;

      pEdit->GetWindowText(str);

      if(str.GetLength() > 0)

      {

         m_ctrTree.SetItemText(pTVDispInfo->item.hItem, str);

      }

   }



14. 이미지 리스트 설정

   + CImageList  m_Image;      // 32 x 16 아이콘 BITMAP 16 x 16 2개 짜리



   m_Image.m_hImageList = ImageList_LoadImage(

                                          (HINSTANCE) GetWindowLong(m_hWnd, GWL_HINSTANCE),

                                          MAKEINTRESOURCE(IDB_BITMAP_SMALL), 16, 2,

                                          RGB(255,255,255), IMAGE_BITMAP, LR_CREATEDIBSECTION);

   m_ctrTree.SetImageList(&m_Image, TVSIL_NORMAL);















- 드래그 앤 드롭 사용하기







1. 드래그 시작



   - 트리컨트롤의 TVN_BEGINDRAG메시지 사용



   CImageList  *m_pTreeDragImage = NULL; // 드래그시 생성된 이미지 사용

   HTREEITEM  m_hDragItem = NULL;           // 드래그시 처음 선택된 아이템 핸들 기억용







   void CDlg::OnBegindragTree(NMHDR* pNMHDR, LRESULT* pResult)

   {

      NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

      // TODO: Add your control notification handler code here



      // 드래그 이미지 생성

      if(m_pTreeDragImage) m_pTreeDragImage->DeleteImageList();

      m_pTreeDragImage = m_ctrTree.CreateDragImage(pNMTreeView->itemNew.hItem);







      // 드래그시 사용할 이미지 크기 계산

      RECT  rc;

      m_ctrTree.GetItemRect(pNMTreeView->itemNew.hItem, &rc, TRUE); // 아이콘을 포함하는 크기



      // 드래그를 시작

      m_pTreeDragImage->BeginDrag(0, CPoint(pNMTreeView->ptDrag.x-rc.left+16,



                                                       pNMTreeView->ptDrag.y-rc.top));

      // 드래그 이미지 표시

      m_pTreeDragImage->DragEnter(&m_ctrTree, pNMTreeView->ptDrag);

 

      // 마우스 메시지를 잡아두고

      SetCapture();





      // 현재 선택된 아이템 핸들을 기억

      m_hDragItem = pNMTreeView->itemNew.hItem;





      *pResult = 0;

   }







2. 이동



   - WM_MOUSEMOVE메시지 사용







   void CDlg::OnMouseMove(UINT nFlags, CPoint point)

   {

      // TODO: Add your message handler code here and/or call default

      // 드래그 중이라면

      if(m_pTreeDragImage)

      {



         // 트리컨트롤 기준으로 마우스 좌표 계산

         CPoint  p = point;

         ClientToScreen(&p);

         ::ScreenToClient(m_ctrTree.m_hWnd, &p);







         // 마우스가 위치한 아이템을 검사한다.항목이 트리 뷰 항목위에 있는지 확인하고 그렇다면 항목이 밝게 표시되도록한다.

         HTREEITEM hItem = m_ctrTree.HitTest(p);







         // 밝게 표시된 부분과 현재 선택된 아이템이 틀리다면



         if(hItem != m_ctrTree.GetDropHilightItem())

         {

            // 드래그 이미지 그리기 중지

            m_pTreeDragImage->DragLeave(&m_ctrTree);







            // 새로운 항목을 밝게 표시한다.

            m_ctrTree.SelectDropTarget(hItem);







            // 드래그 이미지를 다시 보여준다.

            m_pTreeDragImage->DragEnter(&m_ctrTree, p);

         }

         else

         {

            m_pTreeDragImage->DragMove(p);

         }

      }







      CDialog::OnMouseMove(nFlags, point);

   }







3. 드롭



   - WM_LBUTTONUP메시지 사용







   void CDlg::OnLButtonUp(UINT nFlags, CPoint point)

   {

      // TODO: Add your message handler code here and/or call default







     // 드래그 중이 었다면



      if(m_pTreeDragImage)

      {



         // 마우스 메시지 캡쳐 기능을 제거한다.



         ReleaseCapture();







         // 드래그 과정을 중단한다.

         m_pTreeDragImage->DragLeave(&m_ctrTree);

         m_pTreeDragImage->EndDrag();

         m_pTreeDragImage->DeleteImageList();

         m_pTreeDragImage = NULL;

 

         // 일단 마지막으로 밝게 표시되었던 항목을 찾는다.

         HTREEITEM hTargetItem = m_ctrTree.GetDropHilightItem();

 

         // 밝게 표시된 드롭 항목의 선택을 취소한다.

         m_ctrTree.SelectDropTarget(NULL);







         // 선택된 항목(아이템)이 있다면



         if(hTargetItem)

         {

            // 선택된 아이템과 이동될 곳의 아이템이 같다면 이동할 필요가 없다.

            if(m_hDragItem != hTargetItem)

            {



               // 현재 자식의 부모 아이템 핸들을 구한다.

               HTREEITEM hParentItem = m_ctrTree.GetNextItem(m_hDragItem,



                                                                                       TVGN_PARENT);







               // 이동하려는 곳이 자신이 직접속한 항목 이라면 이동할 필요가 없다.

               if(hParentItem != hTargetItem)

               {

                  // 트리의 내용을 이동하자.

                  MoveTreeItem(&m_ctrTree, m_hDragItem, hTargetItem);







                  // 이동된 곳의 트리를 확장하자.

                  m_ctrTree.Expand(hTargetItem, TVE_EXPAND);





                  // 이미지도 확장한걸로 바꾸자

                  m_ctrTree.SetItemImage(hTargetItem, 1, 1);

   

                  // 원본 트리의 모든 아이템이 사라졌다면 이미지 그림을 기본으로 바꾸자.

                  HTREEITEM hItem = m_ctrTree.GetChildItem(hParentItem);

                  if(!hItem)

                  {

                     m_ctrTree.SetItemImage(hParentItem, 0, 0);

                  }

               }

            }

         }



         m_hDragItem = NULL;

      }







      CDialog::OnLButtonUp(nFlags, point);



   }







4. 트리 항목(아이템) 이동 함수



   // 아이템 데이터 이동

   BOOL MoveTreeItem(CTreeCtrl *pTree, HTREEITEM hSrcItem, HTREEITEM hDestItem)

   {

      // 이동할 아이템의 정보를 알아내자.

      TVITEM    TV;

      char    str[256];

      ZeroMemory(str, sizeof(str));

      TV.hItem = hSrcItem;

      TV.mask  = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

      TV.pszText = str;

      TV.cchTextMax = sizeof(str);

      m_ctrTree.GetItem(&TV);

      DWORD dwData = pTree->GetItemData(hSrcItem);







      // 아이템을 추가 하자.

      TVINSERTSTRUCT  TI;

      TI.hParent        = hDestItem;

      TI.hInsertAfter   = TVI_LAST;

      TI.item.mask     = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

      TI.item.iImage   = TV.iImage;

      TI.item.iSelectedImage = TV.iSelectedImage;

      TI.item.pszText   = TV.pszText;

      HTREEITEM hItem  = pTree->InsertItem(&TI);

      pTree->SetItemData(hItem, dwData);







      // 현재 아이템에 자식 아이템이 있다면

      HTREEITEM hChildItem = pTree->GetChildItem(hSrcItem);

      if(hChildItem)

      {

         // 자식 아이템이 있다면 같이 이동하자.

         MoveChildTreeItem(pTree, hChildItem, hItem);

      }



      // 확장 여부를 알아서 똑같이 하자.

      TVITEM  item;

      item.mask = TVIF_HANDLE;

      item.hItem = hSrcItem;

      pTree->GetItem(&item);

      if(item.state & TVIS_EXPANDED)

      {

         pTree->Expand(hItem, TVE_EXPAND);

      }







      // 아이템을 선택하자.

      pTree->SelectItem(hItem);







      // 기존 아이템을 제거한다.

      pTree->DeleteItem(hSrcItem);







      return TRUE;

   }











   // 현재 트리의 모든 아이템 데이터 이동

   BOOL MoveChildTreeItem(CTreeCtrl *pTree, HTREEITEM hChildItem,

                                                                       HTREEITEM hDestItem)

   {

      HTREEITEM hSrcItem = hChildItem;







      while(hSrcItem)

      {

         // 이동할 아이템의 정보를 알아내자.

         TVITEM    TV;

         char    str[256];

         ZeroMemory(str, sizeof(str));

         TV.hItem     = hSrcItem;

         TV.mask     = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

         TV.pszText = str;

         TV.cchTextMax = sizeof(str);

         m_ctrTree.GetItem(&TV);

         DWORD dwData = pTree->GetItemData(hSrcItem);







         // 아이템을 추가 하자.

         TVINSERTSTRUCT  TI;

         TI.hParent       = hDestItem;

         TI.hInsertAfter  = TVI_LAST;

         TI.item.mask    = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

         TI.item.iImage   = TV.iImage;

         TI.item.iSelectedImage = TV.iSelectedImage;

         TI.item.pszText   = TV.pszText;

         HTREEITEM hItem  = pTree->InsertItem(&TI);

         pTree->SetItemData(hItem, dwData);







         // 현재 아이템에 자식 아이템이 있다면

         HTREEITEM hChildItem = pTree->GetChildItem(hSrcItem);



                                         // pTree->GetNextItem(hSrcItem, TVGN_CHILD);

         if(hChildItem)

         {

            MoveChildTreeItem(pTree, hChildItem, hItem);

         }







         // 확장 여부를 알아서 똑같이 하자.

         TVITEM  item;

         item.mask = TVIF_HANDLE;

         item.hItem = hSrcItem;

         pTree->GetItem(&item);

         if(item.state & TVIS_EXPANDED)

         {

            pTree->Expand(hItem, TVE_EXPAND);

         }







         // 다음 아이템을 알아보자.

         hSrcItem = pTree->GetNextItem(hSrcItem, TVGN_NEXT);

      }

 

      // 기존 아이템을 제거한다.

      pTree->DeleteItem(hChildItem);



      return TRUE;

   }

댓글 없음: