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

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds