데브피아 어느분이 올리셨던 소스를 조금 손봤는데 거의 동일합니다.
다만, goto를 없앤거, 이미지처리 및 클래스 상속없이 헤더파일만 포함하면
어떤 다이얼로그에서도 동일한 처리가 가능하도록 했습니다. 편의성 위주죠..
사용하고자 하는 dlg cpp 파일에 CTV.h 를 인클루드 하시고, 다이얼로그에서 TVN_BEGINDRAG 메세지 추가해주시고 안에 다음처럼 집어넣으시면 됩니다. (예제 - m_TreeMesh 는 CTreeCtrl의 컨트롤 변수입니다)
이해안되시는것 있으면 댓글 달아주세요 ^^
void CDlgTabMesh::OnTvnBegindragTreeMesh(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
HTREEITEM hDragItem = pNMTreeView->itemNew.hItem;
HTREEITEM hDropTargetItem = CTV::DragItem(&m_TreeMesh, hDragItem, pNMTreeView->ptDrag);
if(hDropTargetItem != NULL) {
HTREEITEM hItem = CTV::CopyItem(&m_TreeMesh, hDragItem, hDropTargetItem); // 복사. 차일드까지.
if(hItem != NULL) {
m_TreeMesh.SelectItem(hItem); // 새 아이템 선택
CTV::DeleteItem(&m_TreeMesh, hDragItem); // 기존것 삭제.(차일드까지)
}
}
*pResult = 0;
}
아래는 CTV.h 소스입니다. 선언부.
#pragma once
#include "resource.h"
#include "afxcmn.h"
#include "afxwin.h"
#define ID_TIMER_AUTOEXPAND 1000 // 오토 익스팬드를 위한 타이머 ID이자 시간
#define ID_TIMER_AUTOSCROLL 50 // 오토 스크롤을 위한 타이머 ID이자 시간
typedef class CTV {
public:
static HTREEITEM DragItem(CTreeCtrl* pTree, HTREEITEM hDragItem, CPoint point);
static HTREEITEM CopyItem(CTreeCtrl* pTree, HTREEITEM hSrc, HTREEITEM hDestParent = TVI_ROOT, HTREEITEM hCopyAfter = TVI_LAST);
static void DeleteItem(CTreeCtrl* pTree, HTREEITEM hItem);
} *PCTV, **LPCTV;
아래는 CTV.cpp 입니다. 정의부.
더보기 접기
#include "stdafx.h"
#include "CTV.h"
// devpia 어느분의 함수 개조.
HTREEITEM CTV::DragItem(CTreeCtrl* pTree, HTREEITEM hDragItem, CPoint point)
{
if(GetCapture() != NULL) return NULL; // 이미 다른 윈도가 마우스 캡처 중이면,,,즐;;;
AfxLockTempMaps(); // MFC 어플 프렘웍의 메시지 맵을 잠시 막음
pTree->SetCapture(); // 마우스 캡처 시작
pTree->Invalidate(FALSE); // 컨트롤을 다시 그림
CImageList* pDragImage = new CImageList();
pDragImage->Create(16, 16, ILC_MASK | ILC_COLOR32, 0, 0);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1); // 리소스에 32비트 16x16 이미지 만들어두어야 합니다.
pDragImage->Add(&bitmap, RGB(255,255,255));
if(pDragImage == NULL) {ReleaseCapture(); AfxUnlockTempMaps(FALSE); return NULL;}
// 드랙 처리 시작
CRect rect;
CWnd* pWnd = pTree->GetCapture();
pTree->GetItemRect(hDragItem, &rect, TRUE);
//rect.left -= pTree->GetIndent();
pDragImage->BeginDrag(0, point - rect.TopLeft());
pDragImage->DragEnter(pWnd, point);
pTree->SelectDropTarget(NULL);
HTREEITEM hDropTargetItem = NULL;
MSG msg;
BOOL bExit = false;
while(GetMessage(&msg, NULL, 0, 0)) {
if(pWnd != pTree->GetCapture() || bExit) break;
switch(msg.message) {
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
bExit = true;
break;
case WM_MOUSEMOVE: // 왜 마우스 좌표가 틀리지?
point.x = (short)LOWORD(msg.lParam);
point.y = (short)HIWORD(msg.lParam);
hDropTargetItem = pTree->HitTest(point);
// 자신이나 자기 차일드에 드롭 방지
if((hDropTargetItem == hDragItem) || (hDropTargetItem == pTree->GetParentItem(hDragItem))) hDropTargetItem = NULL;
else { // 드롭
HTREEITEM hItem = hDropTargetItem;
while((hItem = pTree->GetParentItem(hItem)) != NULL) {
if(hItem == hDragItem) {
hDropTargetItem = NULL;
break;
}
}
}
if(hDropTargetItem != pTree->GetDropHilightItem()) {
SetCursor(AfxGetApp()->LoadStandardCursor((hDropTargetItem == NULL) ? IDC_NO : IDC_ARROW));
pDragImage->DragShowNolock(FALSE);
pTree->SelectDropTarget(hDropTargetItem);
pDragImage->DragShowNolock(TRUE);
pTree->SetTimer(ID_TIMER_AUTOEXPAND, ID_TIMER_AUTOEXPAND, NULL); // 오토 익스팬드를 위한 타이머를 설치합니답
}
// 오토 스크롤 영역 테스트를 위해서 컨트롤의 위치를 얻습니답
pTree->GetClientRect(&rect);
if(((point.x >= rect.left) && (point.x <= rect.right)) && ((point.y < rect.top + 10) || (point.y > rect.bottom)))
{
pTree->SetTimer(ID_TIMER_AUTOSCROLL, ID_TIMER_AUTOSCROLL, NULL); // 오토 스크롤을 위한 타이머를 설치합니답
}
pDragImage->DragMove(point);
break;
case WM_TIMER:
if(msg.wParam == ID_TIMER_AUTOEXPAND) // 오토 익스팬드 처리
{
hDropTargetItem = pTree->GetDropHilightItem();
if(hDropTargetItem != NULL) {
pDragImage->DragShowNolock(FALSE);
pTree->Expand(hDropTargetItem, TVE_EXPAND);
pDragImage->DragShowNolock(TRUE);
}
pTree->KillTimer(ID_TIMER_AUTOEXPAND);
} else if(msg.wParam == ID_TIMER_AUTOSCROLL) { // 오토 스크롤 처리
GetCursorPos(&point);
pTree->ScreenToClient(&point);
pTree->GetClientRect(&rect);
BOOL bTestX = (point.x >= rect.left) && (point.x <= rect.right);
if(bTestX && (point.y < rect.top)) pTree->SendMessage(WM_VSCROLL, SB_LINEUP);
else if(bTestX && (point.y > rect.bottom)) pTree->SendMessage(WM_VSCROLL, SB_LINEDOWN);
else pTree->KillTimer(ID_TIMER_AUTOSCROLL);
}
break;
case WM_KEYDOWN:
if(msg.wParam == VK_ESCAPE) { // 드랙 취소
pTree->SelectDropTarget(NULL);
bExit = true;
}
break;
default:// 원래의 메시지 핸들러에게 메시지를 넘깁니답
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
}
pTree->KillTimer(ID_TIMER_AUTOSCROLL);
pTree->KillTimer(ID_TIMER_AUTOEXPAND);
pDragImage->DragLeave(pWnd);
pDragImage->EndDrag();
delete pDragImage;
pTree->Invalidate(FALSE);// 화면을 갱신하고
ReleaseCapture();// 마우스 캡처를 중지하고
AfxUnlockTempMaps(FALSE);// 잠시 막아놨던 메시지 맵을 풉니답
hDropTargetItem = pTree->GetDropHilightItem();// 지정된 드롭 타겟 아이템을 백업 해두고
pTree->SelectDropTarget(NULL);// 초기화 합니답
return hDropTargetItem; // 백업해둔 드롭 타겟 아이템을 리턴하면 끝 ^0^//
}
HTREEITEM CTV::CopyItem(CTreeCtrl* pTree, HTREEITEM hSrc, HTREEITEM hDestParent, HTREEITEM hCopyAfter)
{
TCHAR lpszText[256];
TVINSERTSTRUCT tvis;
ZeroMemory(&tvis, sizeof(tvis));
tvis.hParent = hDestParent;
tvis.hInsertAfter = hCopyAfter;
tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_PARAM;
tvis.item.hItem = hSrc;
tvis.item.pszText = lpszText;
tvis.item.cchTextMax = 256;
tvis.item.stateMask = TVIS_BOLD | TVIS_CUT | TVIS_EXPANDED;
pTree->GetItem(&tvis.item);
HTREEITEM hCopyItem = pTree->InsertItem(&tvis);
if(hCopyItem == NULL) return NULL;
HTREEITEM hItem = pTree->GetChildItem(hSrc);
while(hItem != NULL) {
CopyItem(pTree, hItem, hCopyItem);
hItem = pTree->GetNextSiblingItem(hItem);
}
return hCopyItem;
}
void CTV::DeleteItem(CTreeCtrl* pTree, HTREEITEM hItem)
{
HTREEITEM hChildItem = pTree->GetChildItem(hItem);
while(hChildItem != NULL) { // 차일드부터 삭제.
DeleteItem(pTree, hChildItem);
hChildItem = pTree->GetNextSiblingItem(hChildItem);
}
pTree->DeleteItem(hItem); // 차일드부터 삭제.
}
접기