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 |