Nov 25 2005
[MFC]CListCtrl에 Drag & Drop 기능을 추가하는 방법
Trackback : http://dev.heartsavior.net/archives/138/trackback/
CListCtrl의 Drag & Drop
- CListCtrl의 row를 Drag & Drop 하는 경우를 말한다.
- 드래그 시작 시에 LVN_BEGINDRAG가, 드래그 중에 WM_MOUSEMOVE가, 드래그가 끝날 때 WM_LBUTTONUP이 발생한다
- 각 상황에 대한 처리 방법은 아래의 예제 코드를 참조
예제 코드 열기..
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
// 드래그가 시작된 리스트 컨트롤과 전달인자를 넘김
OnBegindrag(&m_listEditing, pNMHDR);
*pResult = 0;
}
void CMG_HW1Dlg::OnBegindrag(CListCtrl *list, NMHDR *pNMHDR)
{
// 선택된 행을 구한다.
if( list->GetSelectedCount() <= 0 )
return;
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// 드래그 중임을 표시하고 해당 리스트 컨트롤을 보관
m_bDrag = TRUE;
m_hDragWnd = list;
CPoint pt;
// pNMListView->ptAction은 현재 마우스 좌표(hotspot)
CPoint hotSpotPt(pNMListView->ptAction);
// Drag할 동안 보여줄 이미지를 얻어온다.
// 이 때, 이미지의 상단 좌측 좌표를 같이 얻는다.
m_pImageList = CreateDragImageEx(list, &pt);
if( m_pImageList == NULL )
return;
// 이미지가 (0,0)부터 시작한다고 했을 때의 상대적인 좌표로 만들기 위해 뺄셈 수행
hotSpotPt -= pt;
// 드래그 시작 알림
m_pImageList->BeginDrag(0, hotSpotPt);
// 주어진 윈도우가 Update를 못하게 Lock을 걸어버림
m_pImageList->DragEnter(NULL, pNMListView->ptAction);
// 드래그 시에 드래그 이미지를 보여주도록 설정 ? 윈도우에 Lock을 걸지 않음
m_pImageList->DragShowNolock(true);
// 마우스 이벤트 캡쳐 ? 좌표에 상관없이 모든 마우스 이벤트를 잡아냄
SetCapture();
}
void CMG_HW1Dlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// 현재 리스트 컨트롤에서 드래그 중인 경우
if( m_bDrag && m_hDragWnd )
{
// 받아온 점을 스크린 좌표계로 변환
CPoint screenPt(point);
ClientToScreen(&screenPt);
// 드래그 이미지를 이동
m_pImageList->DragMove(screenPt);
// 현재 좌표에 어떤 윈도우가 있는지 알아냄
CWnd * hDropWnd = WindowFromPoint(screenPt);
// 현재 좌표가 전체 윈도우에 속하는지 체크
CRect winRect;
GetWindowRect(&winRect);
BOOL bOutOfWindow = !(winRect.PtInRect(screenPt));
// 등록 파일 -> FTP
if( m_hDragWnd == &m_listRegistered && hDropWnd == &m_listFtpFile )
// 커서를 IDC_ARROW로 설정(허용된다는 의미)
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
// FTP -> 등록 파일
else if( m_hDragWnd == &m_listFtpFile && hDropWnd == &m_listRegistered )
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
// FTP -> 편집 목록
else if( m_hDragWnd == &m_listFtpFile && hDropWnd == &m_listEditing )
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
// 편집 목록 -> FTP
else if( m_hDragWnd == &m_listEditing && hDropWnd == &m_listFtpFile )
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
// 등록 해제
else if( m_hDragWnd == &m_listRegistered && bOutOfWindow == TRUE )
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
Else
// 커서를 IDC_NO로 설정(허용되지 않는다는 의미)
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_NO));
}
CDialog::OnMouseMove(nFlags, point);
}
void CMG_HW1Dlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// 현재 리스트 컨트롤에서 드래그 중인 경우
if( m_bDrag && m_hDragWnd )
{
// 마우스 캡쳐를 해제
ReleaseCapture();
// 해당 윈도우의 Lock을 풀고 Update 가능한 상태로 만들며 드래그 이미지를 지움
m_pImageList->DragLeave(NULL);
// 드래그 종료
m_pImageList->EndDrag();
CPoint screenPt(point);
ClientToScreen(&screenPt);
CWnd * hDropWnd = WindowFromPoint(screenPt);
CRect winRect;
GetWindowRect(&winRect);
BOOL bOutOfWindow = !(winRect.PtInRect(screenPt));
// 등록 파일 -> FTP
if( m_hDragWnd == &m_listRegistered && hDropWnd == &m_listFtpFile )
OnButtonUpload();
// FTP -> 등록 파일
else if( m_hDragWnd == &m_listFtpFile && hDropWnd == &m_listRegistered )
OnButtonDownload();
// FTP -> 편집 목록
else if( m_hDragWnd == &m_listFtpFile && hDropWnd == &m_listEditing )
OnButtonEdit();
// 편집 목록 -> FTP
else if( m_hDragWnd == &m_listEditing && hDropWnd == &m_listFtpFile )
OnButtonApplyedit();
// 등록 해제
else if( m_hDragWnd == &m_listRegistered && bOutOfWindow == TRUE )
OnButtonUnregister();
// 드래그 중이 아님으로 설정
m_bDrag = FALSE;
m_hDragWnd = NULL;
// 사용한 드래그 이미지 리스트를 해제
delete m_pImageList;
m_pImageList = NULL;
}
CDialog::OnLButtonUp(nFlags, point);
}
//////////////////////////////////////////////////////////////////////////
// Copied by KpsFtp
CImageList * CMG_HW1Dlg::CreateDragImageEx(CListCtrl *pList, LPPOINT lpPoint)
{
if (pList->GetSelectedCount() <= 0)
return NULL;
CRect rectSingle;
CRect rectComplete(0,0,0,0);
// Determine List Control Client width size
pList->GetClientRect(rectSingle);
int nWidth = rectSingle.Width();
// Start and Stop index in view area
/* 현재 보이는 아이템의 시작 인덱스와 끝 인덱스를 구한다. */
int nIndex = pList->GetTopIndex() - 1;
int nBottomIndex = pList->GetTopIndex() + pList->GetCountPerPage() - 1;
if (nBottomIndex > (pList->GetItemCount() - 1))
nBottomIndex = pList->GetItemCount() - 1;
// Determine the size of the drag image (limit for rows visibled and Client width)
while ((nIndex = pList->GetNextItem(nIndex, LVNI_SELECTED)) != -1)
{
if (nIndex > nBottomIndex)
break;
pList->GetItemRect(nIndex, rectSingle, LVIR_BOUNDS);
if (rectSingle.left < 0)
rectSingle.left = 0;
if (rectSingle.right > nWidth)
rectSingle.right = nWidth;
rectComplete.UnionRect(rectComplete, rectSingle);
}
CClientDC dcClient(this);
/* CDC* dcClient=GetDC(); : 동일 */
CDC dcMem;
CBitmap Bitmap;
if (!dcMem.CreateCompatibleDC(&dcClient))
return NULL;
if (!Bitmap.CreateCompatibleBitmap(&dcClient, rectComplete.Width(), rectComplete.Height()))
return NULL;
CBitmap *pOldMemDCBitmap = dcMem.SelectObject(&Bitmap);
// Use green as mask color
dcMem.FillSolidRect(0, 0, rectComplete.Width(), rectComplete.Height(), RGB(0,255,0));
// Paint each DragImage in the DC
nIndex = pList->GetTopIndex() - 1;
while ((nIndex = pList->GetNextItem(nIndex, LVNI_SELECTED)) != -1)
{
if (nIndex > nBottomIndex)
break;
CPoint pt;
CImageList* pSingleImageList = pList->CreateDragImage(nIndex, &pt);
if (pSingleImageList)
{
pList->GetItemRect(nIndex, rectSingle, LVIR_BOUNDS);
/* nIndex번째 이미지리스트의 이미지(0번째)를
메모리 DC에 그린다.
*/
pSingleImageList->Draw( &dcMem, 0, CPoint(rectSingle.left - rectComplete.left, rectSingle.top - rectComplete.top), ILD_MASK);
pSingleImageList->DeleteImageList();
delete pSingleImageList;
}
}
dcMem.SelectObject(pOldMemDCBitmap);
CImageList* pCompleteImageList = new CImageList;
pCompleteImageList->Create(rectComplete.Width(), rectComplete.Height(), ILC_COLOR | ILC_MASK, 0, 1);
pCompleteImageList->Add(&Bitmap, RGB(0, 255, 0)); // Green is used as mask color
Bitmap.DeleteObject();
if (lpPoint)
{
lpPoint->x = rectComplete.left;
lpPoint->y = rectComplete.top;
}
return pCompleteImageList;
}[/code]
