CreateDragImage for multiple selected items in CListCtrl

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Sample Image

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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read