CreateDragImage for multiple selected items in CListCtrl

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


Comments

  • How about custom drag image?

    Posted by Legacy on 01/26/2004 12:00am

    Originally posted by: Steven

    Hi everybody,

    Vladimir's comment only partly solves my problem. You see, I want to perform a drag&drop operation, but want the drag image to be a custom string. The strng would be a composition of 4 columns in the list control, but I don't want the unnecessry space between the columns in the drag image. Can anyone help me with this???

    Reply
  • Great! ... but need little repair for LVS_EX_FULLROWSELECT

    Posted by Legacy on 11/05/2003 12:00am

    Originally posted by: Vladimir

    I have a ListCtrl with above stile, so, when I create an ImageList, I get full text view for current selected row(as it shown in ListCtrl) in my ImageList. Not so nice picture...
    
    Ok, this is my solution:

    CImageList* CDDListCtrl::CreateDragImageEx(int item, int iSubItem)
    {
    CRect rcItem(0,0,0,0);
    CRect rcSubItem(0,0,0,0);
    BOOL bFirst = TRUE;

    // Determine the rectangle of subItem
    GetSubItemRect(item, iSubItem, LVIR_LABEL, rcSubItem);

    // Create bitmap in memory DC
    CClientDC cDc(this);
    CDC cMemDC;
    CBitmap cBitmap;

    if(!cMemDC.CreateCompatibleDC(&cDc))
    return NULL;

    if(!cBitmap.CreateCompatibleBitmap(&cDc, rcSubItem.Width(), rcSubItem.Height()))
    return NULL;

    CBitmap* pOldMemDCBitmap = cMemDC.SelectObject( &cBitmap );
    // Here green is used as mask color
    cMemDC.FillSolidRect(0,0,rcSubItem.Width(), rcSubItem.Height(), RGB(0, 255, 0));

    GetItemRect(item, rcItem, LVIR_BOUNDS);

    CPoint cPt;
    CImageList *pFullImgList = CreateDragImage(item, &cPt);

    // Paint clipped DragImage in the DC
    //in this place we cut full row view and get only the part we need
    pFullImgList->DrawIndirect(&cMemDC,
    0,
    CPoint( rcItem.left-rcSubItem.left,
    rcItem.top-rcSubItem.top ),
    rcItem.Size(),
    CPoint(0,0));
    delete pFullImgList;

    cMemDC.SelectObject( pOldMemDCBitmap );
    // Create the imagelist with the clipped image
    CImageList* pShortImgList = new CImageList;

    pShortImgList->Create(rcSubItem.Width(),
    rcSubItem.Height(),
    ILC_COLOR | ILC_MASK, 0, 1);
    // Here green is used as mask color
    pShortImgList->Add(&cBitmap, RGB(0, 255, 0));

    cBitmap.DeleteObject();

    return pShortImgList;
    }

    • Sorry, but this code DOES NOT support multiple selected items...

      Posted by PatrikE on 09/09/2005 07:07am

      It is a little bit misleading to post that code under this article, since one would expect code here that DOES support that feature. Besides: It also does NOT display the icon of the selected row. This reply is intended to inform readers of Vladimirs post about the capabilities of his code and not to bash him in any way.

      Reply
    Reply
  • Modification for Owner drawn List Views

    Posted by Legacy on 02/21/2000 12:00am

    Originally posted by: Maurizio Maccani

    If you have a OwnerDrawn List View the code must change a little:
    
    

    CImageList *pSingleImageList;
    CPoint cPt;


    pos = GetFirstSelectedItemPosition();
    cMemDC.SelectObject(GetFont());
    while (pos)
    {
    nIdx = GetNextSelectedItem( pos );
    GetItemRect( nIdx, cSingleRect, LVIR_BOUNDS );
    CRect cIconRect;
    GetItemRect( nIdx, cIconRect, LVIR_ICON );
    int cx=cIconRect.Width();
    pSingleImageList = CreateDragImage( nIdx, &cPt);
    if (pSingleImageList)
    {
    pSingleImageList->DrawIndirect( &cMemDC,
    0,
    CPoint( cSingleRect.left-cCompleteRect.left,
    cSingleRect.top-cCompleteRect.top ),
    cSingleRect.Size(),
    CPoint(0,0));
    cSingleRect.left+=cx-cCompleteRect.left;
    cSingleRect.right-=cCompleteRect.left;
    cSingleRect.top-=cCompleteRect.top;
    cSingleRect.bottom-=cCompleteRect.top;
    CString sLabel = GetItemText( nIdx, 0 );

    int ret = cMemDC.DrawText(sLabel ,&cSingleRect,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP
    | DT_VCENTER | DT_END_ELLIPSIS);

    delete pSingleImageList;
    }
    }

    Reply
  • Functions undefined...

    Posted by Legacy on 02/17/2000 12:00am

    Originally posted by: Daniel Larocque

    I would like to use your code but I can't get it to compile.

    Where is GetFirstSelectedItemPosition (...) supposed to be defined?

    Where is DrawIndirect (...) supposed to be defined?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds