
Environment: VC6 SP2, NT4 SP4
Currently the CreateDragImage method from CListCtrl only supports the generation of drag images of a single selected item (at least I didn’t found an alternative in the Win API).
Inspired by an article from Pel K Txnder I found one way to create drag images for multiple selections.
The trick is to create a bitmap in a memory dc and paint the drag image from each selected item into it.
This bitmap is added to the drag imagelist.
The code enhances a derived CListCtrl (CListCtrlEx) with the method CreateDragImageEx.
However, it should also work with a few modifications without subclassing the CListCtrl.
CImageList *CListCtrlEx::CreateDragImageEx( LPPOINT lpPoint )
{
CRect cSingleRect;
CRect cCompleteRect( 0,0,0,0 );
int nIdx;
BOOL bFirst = TRUE;
//
// Determine the size of the drag image
//
POSITION pos = GetFirstSelectedItemPosition();
while (pos)
{
nIdx = GetNextSelectedItem( pos );
GetItemRect( nIdx, cSingleRect, LVIR_BOUNDS );
if (bFirst)
{
// Initialize the CompleteRect
GetItemRect( nIdx, cCompleteRect, LVIR_BOUNDS );
bFirst = FALSE;
}
cCompleteRect.UnionRect( cCompleteRect, cSingleRect );
}
//
// Create bitmap in memory DC
//
CClientDC cDc(this);
CDC cMemDC;
CBitmap cBitmap;
if(!cMemDC.CreateCompatibleDC(&cDc))
return NULL;
if(!cBitmap.CreateCompatibleBitmap(&cDc, cCompleteRect.Width(), cCompleteRect.Height()))
return NULL;
CBitmap* pOldMemDCBitmap = cMemDC.SelectObject( &cBitmap );
// Here green is used as mask color
cMemDC.FillSolidRect(0,0,cCompleteRect.Width(), cCompleteRect.Height(), RGB(0, 255, 0));
//
// Paint each DragImage in the DC
//
CImageList *pSingleImageList;
CPoint cPt;
pos = GetFirstSelectedItemPosition();
while (pos)
{
nIdx = GetNextSelectedItem( pos );
GetItemRect( nIdx, cSingleRect, LVIR_BOUNDS );
pSingleImageList = CreateDragImage( nIdx, &cPt);
if (pSingleImageList)
{
pSingleImageList->DrawIndirect( &cMemDC,
0,
CPoint( cSingleRect.left-cCompleteRect.left,
cSingleRect.top-cCompleteRect.top ),
cSingleRect.Size(),
CPoint(0,0));
delete pSingleImageList;
}
}
cMemDC.SelectObject( pOldMemDCBitmap );
//
// Create the imagelist with the merged drag images
//
CImageList* pCompleteImageList = new CImageList;
pCompleteImageList->Create( cCompleteRect.Width(),
cCompleteRect.Height(),
ILC_COLOR | ILC_MASK, 0, 1);
// Here green is used as mask color
pCompleteImageList->Add(&cBitmap, RGB(0, 255, 0));
cBitmap.DeleteObject();
//
// as an optional service:
// Find the offset of the current mouse cursor to the imagelist
// this we can use in BeginDrag()
//
if ( lpPoint )
{
CPoint cCursorPos;
GetCursorPos( &cCursorPos );
ScreenToClient( &cCursorPos );
lpPoint->x = cCursorPos.x - cCompleteRect.left;
lpPoint->y = cCursorPos.y - cCompleteRect.top;
}
return( pCompleteImageList );
}
Sample usage:
void CDialogWhichUsesListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
POINT pt;
m_pDragImage = m_ctlList.CreateDragImageEx( &pt );
m_pDragImage->BeginDrag( 0, pt );
m_pDragImage->DragEnter( GetDesktopWindow(), pNMListView->ptAction);
SetCapture();
*pResult = 0;
}
Downloads
Download source (same as above, but with proper formatting) – 1 Kb