MFC 트리 컨트롤 드래그 앤 드롭 기능 구현하기.
다른 라이브러리 사용하지 않고 MFC 기본으로 구현해보자.
일단, 트리 컨트롤의 속성에 다음 값이 FALSE로 드래그&드롭 기능이 되도록 해주어야 한다.
해당 트리 컨트롤의 멤버변수를
m_ctrlTree 로 선언 하였다는 가정하에 진행 해보자.
아이템 추가는 다음과 같이 이루어진다.
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이 리턴된다.
아이템의 확장은 다음과 같다.
m_ctrTree.Expand(hTreeItem, TVE_EXPAND);
선택된 아이템 하이라이트
+ TVN_SELCHANGED메시지를 사용한다. NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; HTREEITEM hTreeItem = pNMTreeView->itemNew.hItem; // 이 값이 선택된 아이템의 핸들이다.
아이템 문자열 가져오기
CString str = m_ctrTree.GetItemText(hTreeItem);
아이템 개수 알아내기
int nCount = m_ctrTree.GetCount();
아이템 제거
m_ctrTree.DeleteItem(hTreeItem); // 핸들 아래단의 아이템들도 모두 제거된다.
현재 선택된 아이템 알아내기
HTREEITEM hTreeItem = m_ctrTree.GetSelectedItem();
위치로 아이템 찾기
CPoint p; GetCursorPos(&p); ::ScreenToClient(m_ctrTree.m_hWnd, &p); HTREEITEM hItem = m_ctrTree.HitTest(p);
아이템 확장 축소 감지
+ 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 { // 축소 }
아이템 아이콘 설정 변경
m_ctrTree.SetItemImage(hTreeItem, 0, 1);
아이템 에디트 입력중 포커스가 나갈때 입력중인 값 아이템에 적용하기
+ 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);
}
}
if(pEdit)
{
CString str;
pEdit->GetWindowText(str);
if(str.GetLength() > 0)
{
m_ctrTree.SetItemText(pTVDispInfo->item.hItem, str);
}
}
이미지 리스트 설정
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);
◎ Drag & Drop 사용 하기
1. 드래그 시작
- 트리컨트롤의 TVN_BEGINDRAG메시지 사용
CImageList *m_pTreeDragImage = NULL; // 드래그시 생성된 이미지 사용 HTREEITEM m_hDragItem = NULL; // 드래그시 처음 선택된 아이템 핸들 기억용 void CXXXDlg::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 CXXXDlg::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 CXXXDlg::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(); delete m_pTreeDragImage; 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; }
'Develope > MFC' 카테고리의 다른 글
IOCP 와 OpenSSL 사용 (공부중) (0) | 2016.07.28 |
---|---|
Memory Allocation 크기의 비밀 (1) | 2013.06.24 |
Warning C4150: 불완전한 형식 'XX::XX' 에 대한 포인터를 삭제했습니다. 소멸자가 호출되지 않습니다. (0) | 2013.06.11 |
Big-endian / Little-endian (0) | 2013.05.20 |
Overlapped IO 와 IOCP 이야기 (4) (0) | 2013.03.13 |
Overlapped IO 와 IOCP 이야기 (3-2) (0) | 2013.03.13 |
Overlapped IO 와 IOCP 이야기 (3-1) (0) | 2013.03.13 |
Overlapped IO 와 IOCP 이야기 (2) (0) | 2013.03.13 |
Overlapped IO 와 IOCP 이야기 (1) (1) | 2013.03.13 |
Visual Leak Detector (Memory Leak 찾기) (0) | 2013.02.14 |