2010년 10월 27일 수요일

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;
   }

댓글 없음:

댓글 쓰기