Pictrue Control实际上就是CStatic,它的动态创建过程如下:
m_Static.Create(_T(""), WS_VISIBLE|WS_CHILD|SS_BITMAP|SS_CENTERIMAGE, CRect(200,200,300,300), this);
HBITMAP hBitMap = ::LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BITMAP_Black));
m_Static.SetBitmap(hBitMap);
然后就是响应鼠标的几个动作:
按下左键:
void CXXXDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CRect rectPic;
POINT ptPut = point;
//GetDlgItem(IDC_STATIC_Image1)->GetWindowRect(rectPic);
m_Static.GetWindowRect(rectPic);
ClientToScreen(&ptPut);
if(rectPic.PtInRect(ptPut))
{
CBitmap bitmapTemp, *pOldBitmap;
CDC *pDC = m_Static.GetDC();
CDC *pMemDC = new CDC;
//创建位图内存
bitmapTemp.CreateCompatibleBitmap(pDC, rectPic.Width(), rectPic.Height());
pMemDC->CreateCompatibleDC(pDC);
pOldBitmap = pMemDC->SelectObject(&bitmapTemp);
pMemDC->BitBlt(0, 0, rectPic.Width(), rectPic.Height(), pDC, 0, 0, SRCCOPY);
pMemDC->SelectObject(pOldBitmap);
delete pMemDC;
ReleaseDC(pDC);
m_bIsLButtonDown = TRUE;
m_ptOffset.x = ptPut.x-rectPic.left;
m_ptOffset.y = ptPut.y-rectPic.top;
m_imgDrag.DeleteImageList();
m_imgDrag.Create(rectPic.Width(), rectPic.Height(), ILC_COLOR32|ILC_MASK, 0, 1);
m_imgDrag.Add(&bitmapTemp, RGB(0, 0, 0));
m_imgDrag.BeginDrag(0, m_ptOffset);
m_imgDrag.DragEnter(NULL, ptPut);
SetCapture();
}
CDialog::OnLButtonDown(nFlags, point);
}
鼠标移动:
void CXXXXDlg::OnMouseMove(UINT nFlags, CPoint point)
{
if(m_bIsLButtonDown)
{
CRect rtClient, rtPicture;
m_ptMove = point;
//GetDlgItem(IDC_STATIC_Image1)->GetWindowRect(rtPicture);
m_Static.GetWindowRect(rtPicture);
GetClientRect(rtClient);
ClientToScreen(&rtClient);
ClientToScreen(&m_ptMove);
if(rtClient.left>m_ptMove.x-m_ptOffset.x)
m_ptMove.x = rtClient.left+m_ptOffset.x;
if(rtClient.top>m_ptMove.y-m_ptOffset.y)
m_ptMove.y = rtClient.top+m_ptOffset.y;
if(rtClient.right-rtPicture.Width()<m_ptMove.x-m_ptOffset.x)
m_ptMove.x = rtClient.right-rtPicture.Width()+m_ptOffset.x;
if(rtClient.bottom-rtPicture.Height()<m_ptMove.y-m_ptOffset.y)
m_ptMove.y = rtClient.bottom-rtPicture.Height()+m_ptOffset.y;
CImageList::DragMove(m_ptMove);
}
CDialog::OnMouseMove(nFlags, point);
}
左键弹起:
void CXXXXDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if(m_bIsLButtonDown)
{
CRect rectPic;
ScreenToClient(&m_ptMove);
m_Static.GetWindowRect(rectPic);
m_Static.MoveWindow(m_ptMove.x-m_ptOffset.x, m_ptMove.y-m_ptOffset.y, rectPic.Width(), rectPic.Height());
m_bIsLButtonDown = FALSE;
CImageList::DragLeave(this);
CImageList::EndDrag();
ReleaseCapture();
m_Static.Invalidate();
}
CDialog::OnLButtonUp(nFlags, point);
}
TreeCtrl也是一样,同样是响应鼠标的几个动作,只不过麻烦一点:
按下左键:
void CDragDropTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
SetFocus();
do
{
HTREEITEM hItem = HitTest(point,&nFlags);
if( nFlags & TVHT_ONITEMBUTTON )
{
// Check if any child present
if(!ItemHasChildren(hItem))
{
break;
}
CTreeCtrl::OnLButtonDown(nFlags,point);
break;
}
if( NULL == hItem )
{
break;
}
if( NULL == GetParentItem(hItem) )
{
break;
}
unsigned short shKeyState = GetKeyState(VK_CONTROL);
shKeyState >>= 15;//get high-order bit
if( shKeyState == 1 )// key down
{
OnControlKeyPress(hItem);
break;
}
else
{
if( m_vSelItem.size() == 0 )
{
SetItemState(hItem,TVIS_SELECTED,TVIS_SELECTED);
m_vSelItem.push_back(hItem);
break;
}
shKeyState = GetKeyState(VK_SHIFT);
shKeyState >>= 15;
if( shKeyState == 1 )
{
OnShiftKeyPress(hItem);
break;
}
}
m_hLastSelItem = hItem;
if( m_vSelItem.size() == 1 )
{
ClearSelection();
SetItemState(m_hLastSelItem,TVIS_SELECTED,TVIS_SELECTED);
m_vSelItem.push_back(m_hLastSelItem);
}
}while(false);
}
其中OnShiftKeyPress是响应shift按键,用来添加一组item,实现如下:
void CDragDropTreeCtrl::OnShiftKeyPress(HTREEITEM hCurItem)
{
if( m_vSelItem.size() > 0 )
{
if( !IsInTheSameLevel(hCurItem) )
{
return;
}
}
HTREEITEM hItemFrom = m_vSelItem[0];
SetItemState(hCurItem,TVIS_SELECTED,TVIS_SELECTED);
SelectItems(hItemFrom,hCurItem);
}
BOOL CDragDropTreeCtrl::IsInTheSameLevel(HTREEITEM hItem)
{
BOOL bInTheSameLevel = TRUE;
SEL_ITEM_LIST::iterator itr;
for( itr = m_vSelItem.begin(); itr != m_vSelItem.end(); ++itr )
{
if( GetParentItem(hItem) != GetParentItem(*itr) )
{
bInTheSameLevel = FALSE;
}
}
return bInTheSameLevel;
}
SelectItems用来将item加入链表或从链表清除:
void CDragDropTreeCtrl::SelectItems(HTREEITEM hItemFrom,HTREEITEM hItemTo)
{
RECT FromRect;
GetItemRect(hItemFrom,&FromRect,FALSE);
RECT ToRect;
GetItemRect(hItemTo,&ToRect,FALSE);
HTREEITEM hTemp;
if( FromRect.top > ToRect.top )
{
hTemp = hItemFrom;
hItemFrom = hItemTo;
hItemTo = hTemp;
}
ClearSelection();
hTemp = hItemFrom;
while(1)
{
SetItemState(hTemp,TVIS_SELECTED,TVIS_SELECTED);
m_vSelItem.push_back(hTemp);
if( hTemp == hItemTo )
{
break;
}
hTemp = GetNextVisibleItem(hTemp);
}
}
void CDragDropTreeCtrl::ClearSelection()
{
int nSelItemCount = m_vSelItem.size();
for( int nIdx = 0; nIdx < nSelItemCount; ++nIdx)
{
SetItemState(m_vSelItem[nIdx],0,TVIS_SELECTED);//全部置0
}
m_vSelItem.clear();
}
OnControlKeyPress用来响应Ctrl按键,用来逐个添加或删除item
void CDragDropTreeCtrl::OnControlKeyPress(HTREEITEM hCurItem)
{
if( m_vSelItem.size() > 0 )
{
if( !IsInTheSameLevel(hCurItem) )
{
return;
}
}
int nState = (TVIS_SELECTED == GetItemState(hCurItem,TVIS_SELECTED))?0:TVIS_SELECTED;
SetItemState(hCurItem,nState,TVIS_SELECTED);
if( 0 == nState )
{
RemoveFromSelectionList(hCurItem);
}
else
{
m_vSelItem.push_back(hCurItem);
}
}
void CDragDropTreeCtrl::RemoveFromSelectionList(HTREEITEM hItem)
{
SEL_ITEM_LIST::iterator itr;
for( itr = m_vSelItem.begin(); itr != m_vSelItem.end(); ++itr )
{
if( (*itr) == hItem )
{
m_vSelItem.erase(itr);
break;
}
}
}
还需要在鼠标点击空白处时取消选中:
void CDragDropTreeCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
//if click is not on any item clear all the selection
int nSelItemCount = m_vSelItem.size();
for( int nIdx = 0; nIdx < nSelItemCount; ++nIdx)
{
SetItemState(m_vSelItem[nIdx],0,TVIS_SELECTED);//全部置0
}
m_vSelItem.clear();
}
移动鼠标:
void CDragDropTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if( ::DragDetect(m_hWnd,point) )
{
OnDrag(point);
}
CTreeCtrl::OnMouseMove(nFlags,point);
}
void CDragDropTreeCtrl::OnDrag(CPoint point)
{
if( m_vSelItem.size() == 0 )//没有拖动item
{
return;
}
m_bDragging = TRUE;
UINT nFlags;
HTREEITEM hItem = HitTest(point,&nFlags);
m_pImageList = CreateDragImageEx();
ASSERT (m_pImageList != NULL);
if (m_pImageList != NULL)
{
CRect rect;
GetItemRect (hItem, rect, TRUE);
POINT pt;
pt.x = rect.left+5;
pt.y = rect.top+5;
ClientToScreen ( &pt ) ;
m_pImageList->BeginDrag (0,CPoint(-5,-5));
m_pImageList->DragEnter (NULL, pt);
SetCapture();
int nEventListenerCount = m_vEventListener.size();
for( int nEventListenerIdx = 0; nEventListenerIdx < nEventListenerCount; ++nEventListenerIdx )
{
m_vEventListener[nEventListenerIdx]->OnDrag();//外部接口实现
}
}
}
其中的关键步骤在于移动中对item的绘制:
CImageList* CDragDropTreeCtrl::CreateDragImageEx()
{
// Find the bounding rectangle of all the selected items
CRect rectBounding; // Holds rectangle bounding area for bitmap
CRect rectFirstItem; // Holds first item's height and width
CRect rectTextArea; // Holds text area of image
int nNumSelected; // Holds total number of selected items
HTREEITEM hItem;
CClientDC DraggedNodeDC(this); // To draw drag image
CDC *pDragImageCalcDC = NULL; // to find the drag image width and height
CString strItemText;
CBitmap *pBitmapOldMemDCBitmap = NULL; // Pointer to bitmap in memory
CFont *pFontOld = NULL; // Used for bitmap font
int nIdx = 0, nCounter = 0; // Counts array elements
int nMaxWidth = 0; // holds the maximum width to be taken to form the bounding rect
//UINT uiSelectedItems; // Holds an item
CImageList *pImageListDraggedNode = NULL; // Holds an image list pointer
nNumSelected = m_vSelItem.size();
if( nNumSelected > 0)
{
pDragImageCalcDC = GetDC();
if(pDragImageCalcDC == NULL)
{
return NULL;
}
CImageList *pImageList = GetImageList(TVSIL_NORMAL);
if (pImageList == NULL)
{
return NULL;
}
//HICON hIcon = pImageList->ExtractIcon(nImg);
int cx,cy;
ImageList_GetIconSize(*pImageList, &cx, &cy);
// Calculate the maximum width of the bounding rectangle
for ( nIdx = 0; nIdx < nNumSelected; nIdx++)
{
// Get the item's height and width one by one
hItem = m_vSelItem[nIdx];
strItemText = GetItemText(hItem);
rectFirstItem.SetRectEmpty();
pDragImageCalcDC->DrawText(strItemText, rectFirstItem, DT_CALCRECT);//得到rect
if(nMaxWidth < ( rectFirstItem.Width()+cx))
{
nMaxWidth = rectFirstItem.Width()+cx;
}
}
// Get the first item's height and width
hItem = m_vSelItem[0];
strItemText = GetItemText(hItem);
rectFirstItem.SetRectEmpty();
pDragImageCalcDC->DrawText(strItemText, rectFirstItem, DT_CALCRECT);
ReleaseDC(pDragImageCalcDC);
// Initialize textRect for the first item
rectTextArea.SetRect(1, 1, nMaxWidth, rectFirstItem.Height());
// Find the bounding rectangle of the bitmap
rectBounding.SetRect(0,0, nMaxWidth+2, (rectFirstItem.Height()+2)*nNumSelected);
CDC MemoryDC; // Memory Device Context used to draw the drag image
// Create bitmap
if( !MemoryDC.CreateCompatibleDC(&DraggedNodeDC) )
{
return NULL;
}
CBitmap DraggedNodeBmp; // Instance used for holding dragged bitmap
if( !DraggedNodeBmp.CreateCompatibleBitmap(&DraggedNodeDC, rectBounding.Width(), rectBounding.Height()) )
{
return NULL;
}
pBitmapOldMemDCBitmap = MemoryDC.SelectObject( &DraggedNodeBmp );
pFontOld = MemoryDC.SelectObject(GetFont());
CBrush brush(RGB(255,255,255));
MemoryDC.FillRect(&rectBounding, &brush);
MemoryDC.SetBkColor(RGB(255,255,255));
MemoryDC.SetBkMode(TRANSPARENT);
MemoryDC.SetTextColor(RGB(0,0,0));
// Search through array list
for( nIdx = 0; nIdx < nNumSelected; nIdx++)
{
hItem = m_vSelItem[nIdx];
int nImg = 0,nSelImg=0;
GetItemImage(hItem,nImg,nSelImg);
HICON hIcon = pImageList->ExtractIcon(nImg);
//cdcMemory.DrawIcon(rectTextArea.left,rectTextArea.top,hIcon);
MemoryDC.MoveTo(rectTextArea.left,rectTextArea.top);
if( nIdx != nNumSelected-1 )
{
MemoryDC.LineTo(rectTextArea.left,rectTextArea.top+18);
}
else
{
MemoryDC.LineTo(rectTextArea.left,rectTextArea.top+8);
}
MemoryDC.MoveTo(rectTextArea.left,rectTextArea.top+8);
MemoryDC.LineTo(rectTextArea.left+5,rectTextArea.top+8);
int nLeft = rectTextArea.left;
rectTextArea.left += 3;
::DrawIconEx(MemoryDC.m_hDC,rectTextArea.left,rectTextArea.top,hIcon,\
16,16,0,NULL,DI_NORMAL);
rectTextArea.left += cx;
MemoryDC.Rectangle(rectTextArea);
MemoryDC.DrawText(GetItemText(hItem), rectTextArea, DT_LEFT| DT_SINGLELINE|DT_NOPREFIX);
rectTextArea.left = nLeft;
rectTextArea.OffsetRect(0, rectFirstItem.Height()+2);
DestroyIcon(hIcon);
}
MemoryDC.SelectObject( pFontOld );
MemoryDC.SelectObject( pBitmapOldMemDCBitmap );
MemoryDC.DeleteDC();
// Create imagelist
pImageListDraggedNode = new CImageList;
pImageListDraggedNode->Create(rectBounding.Width(), rectBounding.Height(),\
ILC_COLOR | ILC_MASK, 0, 1);
pImageListDraggedNode->Add(&DraggedNodeBmp, RGB(255, 255,255));
return pImageListDraggedNode;
}
return NULL;
}
最后是响应左键弹起:
void CDragDropTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
if( !(MK_CONTROL&nFlags) && !(MK_SHIFT&nFlags) )
{
if( m_vSelItem.size() > 1 )
{
ClearSelection();
SetItemState(m_hLastSelItem,TVIS_SELECTED,TVIS_SELECTED);
m_vSelItem.push_back(m_hLastSelItem);
}
}
if ( m_bDragging && m_pImageList != NULL )
{
m_bDragging = FALSE;
//KillTimer(1);
ReleaseCapture();
//
// Terminate the dragging operation and release the mouse.
//
m_pImageList->DragLeave (this);
m_pImageList->DragLeave (GetParent());
m_pImageList->EndDrag ();
SelectDropTarget(NULL);
delete m_pImageList;
m_pImageList = NULL;
HTREEITEM hItem = HitTest(point,&nFlags);//得到鼠标松开时落点的item
int nListenerCount = m_vEventListener.size();
for( int nListenerIdx = 0; nListenerIdx < nListenerCount; ++nListenerIdx )
{
m_vEventListener[nListenerIdx]->OnDragRelease(point,hItem);//外部接口实现
}
}
CTreeCtrl::OnLButtonUp(nFlags, point);
}