ListView Drag Image

The place for threads about TimoSoft ExplorerListView.
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

ListView Drag Image

Post by cordov »

Hi everyone.
I'm desperately looking for an help.
I saw your old post to http://www.ureader.com/message/1283952.aspx
Well, I' m creating a custom listview. To allow the image dragging I follow the example in http://www.codeproject.com/KB/miscctrl/ ... print=true
It works fine, except for the font of the text of the listviewitem that is the system one and not the one I chose.Now I couldn't fix it in any way, because if I send to the dodragdrop an image made with all my images the method automatically add an alpha blending also in the space that should be transparent.
Now I'm trying to get the DI_GETDRAGIMAGE. I'm able to handle it, but when I try to put my images in the message.LParam something it's wrong and no transparent images are shown. My be the error is in the wrapper.
Could you spot something wrong here?

Code: Select all

 protected override void WndProc(ref Message message)
   {
           //Handle DI_GETDRAGIMAGE

            if (message.Msg == 49210)
            {
               if (this.SelectedItems[0].ImageIndex >= 0)
               {
                  Bitmap bmp = new Bitmap(this.LargeImageList.Images[this.SelectedItems[0].ImageIndex]);

                  message.LParam = ShellUtils.CreateDragImage.CreateBitMapHandle(bmp.GetHbitmap(), new Point(10, 10), System.Drawing.Color.Transparent);
                    
                  return;
              }
            }
            base.WndProc(ref message);

    } 
And in the wrapper (in C++) I have

Code: Select all

        IntPtr CreateDragImage::CreateBitMapHandle(IntPtr hBmp, Point pt, Color trans)
	{
		OleInitialize(0);
		HBITMAP hbm = (HBITMAP)hBmp.ToPointer();
		BITMAP      bm;
		GetObject(hbm, sizeof(bm), &bm);

		SHDRAGIMAGE *ptrToDragStruc = new SHDRAGIMAGE;

		ptrToDragStruc->sizeDragImage.cx = bm.bmWidth;
		ptrToDragStruc->sizeDragImage.cy = bm.bmHeight;
		ptrToDragStruc->hbmpDragImage =  hbm;
		ptrToDragStruc->crColorKey = RGB(trans.R, trans.G, trans.B); //transparent color
		
		ptrToDragStruc->ptOffset.x = pt.X; 
		ptrToDragStruc->ptOffset.y = pt.Y;

		OleUninitialize();

		return (IntPtr)ptrToDragStruc;
		
	} 

Do you have any idea that I can use? do you think there is something wrong? I don't have problem to change everything, it's important it will work
Thank you
Luigi
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Re: ListView Drag Image

Post by TiKu »

cordov wrote:

Code: Select all

if (message.Msg == 49210)
You shouldn't do that. The DI_GETDRAGIMAGE message isn't guaranteed to be 49210. Use this code instead:

Code: Select all

if (message.Msg == RegisterWindowMessage(DI_GETDRAGIMAGE))
cordov wrote:

Code: Select all

message.LParam = ShellUtils.CreateDragImage.CreateBitMapHandle(bmp.GetHbitmap(), new Point(10, 10), System.Drawing.Color.Transparent);
Don't set LParam to something new. A pointer to a SHDRAGIMAGE struct is passed to your app in LParam and this struct must be used.
I don't know how to do this in C#, but in C++ it would look like this:

Code: Select all

SHDRAGIMAGE* ptrToDragStruc = reinterpret_cast<SHDRAGIMAGE*>(lParam);
ptrToDragStruc->hbmpDragImage =  hbm;
ptrToDragStruc->crColorKey = RGB(trans.R, trans.G, trans.B);
HTH
TiKu
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

Re: Clarification

Post by cordov »

Thank you for your reply.

I need to do that

Code: Select all

 if (message.Msg == 49210) 
because the message is registered in the dll that execute the shell extention and the function RegisterWindowMessage is not part of the .NET 2.0, so I can't use it. Anyway, this solution looks like working because the message is always captured by my custom listview and during the esecution of the InitializeFromWindow (in the shell extention (or dll how I called it before)).

I need to set the LParam, because I want to change the image passed by Listview during the InitializeFromWindow, but I'm not sure that the wrapper I used is right.

If you want you can contact me to cordov@libero.it (msn) and if we can find a solution I'll update the post with our decisions and steps.

Thank you very much
Luigi
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

Post by cordov »

Sorry I saw an other your post..
http://forums.microsoft.com/MSDN/ShowPo ... 1&SiteID=1
Maybe you break your head wih this damn listview so many time, that you can understand my frustration :)
Is it posssible to have a part of the code that show how you get the message and how you handle it?
Maybe the problem is that my code is all in C# and just the dll of the shell extention is in C++ (so my listview is declared in C#), so that's way I'm having all these troubles.
just to know, could you obtain the Vista Explorer style dragging image?

Thank you for your precious help
Luigi
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Re: Clarification

Post by TiKu »

cordov wrote:I need to do that

Code: Select all

 if (message.Msg == 49210) 
because the message is registered in the dll that execute the shell extention and the function RegisterWindowMessage is not part of the .NET 2.0, so I can't use it. Anyway, this solution looks like working because the message is always captured by my custom listview and during the esecution of the InitializeFromWindow (in the shell extention (or dll how I called it before)).
You said it: It looks like working. You can use RegisterWindowMessage in C# using P/Invoke. And you really should.
cordov wrote:because the message is registered in the dll that execute the shell extention
...sounds like you're assuming that RegisterWindowMessage will register the message again on each call. That's not right. Once the message has been registered by any program, RegisterWindowMessage just returns the message code (in your case 49120) on each subsequent call.
cordov wrote:I need to set the LParam, because I want to change the image passed by Listview during the InitializeFromWindow, but I'm not sure that the wrapper I used is right.
I'm not sure I understand this right. Are you using your own implementations of IDragSourceHelper and IDropTargetHelper? Then it's clear you don't see any drag image. You must use the system's implementations:

Code: Select all

IDragSourceHelper* pDragSourceHelper = NULL;
CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_ALL, IID_IDragSourceHelper, reinterpret_cast<LPVOID*>(&pDragSourceHelper));

// use it...

pDragSourceHelper->Release();

IDropTargetHelper* pDropTargetHelper = NULL;
CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_ALL, IID_IDropTargetHelper, reinterpret_cast<LPVOID*>(&pDropTargetHelper));

// use it...

pDropTargetHelper->Release();
Also you must use the SHDRAGIMAGE struct passed to you and modify its members, if you want to see any drag image.
cordov wrote:If you want you can contact me to cordov@libero.it (msn) and if we can find a solution I'll update the post with our decisions and steps.
I don't have a MSN account and won't create one, sorry.
cordov wrote:just to know, could you obtain the Vista Explorer style dragging image?
Yes, I got it working. But I got the required details directly from Microsoft through a MSDN subscription and am not sure whether I'm allowed to publish them. I'll find out.
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Post by TiKu »

By the way, you mentioned a shell extension. Are you writing a shell extension? If so you should know that shell extensions are loaded into the Explorer process and any other process that browses the shell (it's enough if the process uses the Open File common dialog) and that there can't exist two versions of the .net runtime within the same process. Or better: There can, but the results are unpredictable.
So, if your shell extension involves any .net code and any .net process happens to load your extension and this app uses a different version of .net than your extension, you're screwed. That's the reason why writing shell extensions using .net is absolutely unsupported and Microsoft discourages people from doing so. I guess that's also the main reason why Vista's Explorer is still a native app, although in early Vista alpha/beta stages it was supposed to be a .net app.
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

Post by cordov »

Hi timo,
thank you for your reply.
I created a shell extention to have the transparent dragged Images, as windows explore does.
i took the code from http://www.codeproject.com/KB/miscctrl/ ... print=true,
it works for a listView control, but gives problem using a custom drawn listview (during the drag operation, the text is drawn, but with the font of the system the same of explorer, not the one I chose for the listview).
I used this approach, because I don't know how to create the "transparent" image without flickering in pure .Net. (maybe with PInvoke, but I could have problem with the dataobject sent)
Reading the (poor) microzzoz documentation, I see that if I take the message DI_GETDRAGIMAGE, I could send a custom image.
The problem I think to have it's when I pass the pointer between the wrapper in C++ and the C# code.
Maybe my approch is totally wrong, so I could also starting again, but I'm afraid that also using PInvoke, I will have the same the problem with the custom listview.

Thank you for your time and your kindness

Luigi
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

Post by cordov »

An other question... Have you ever drawn by yourself the "transparent" images with the approch to collect the message and modify the LParam? Have you ever experienced if it really works?

Thank you
Luigi
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Post by TiKu »

cordov wrote:I created a shell extention to have the transparent dragged Images, as windows explore does.
i took the code from http://www.codeproject.com/KB/miscctrl/ ... print=true,
it works for a listView control, but gives problem using a custom drawn listview (during the drag operation, the text is drawn, but with the font of the system the same of explorer, not the one I chose for the listview).
I used this approach, because I don't know how to create the "transparent" image without flickering in pure .Net. (maybe with PInvoke, but I could have problem with the dataobject sent)
Wow, that's a really ...erm... strange approach. :crazy:
cordov wrote:An other question... Have you ever drawn by yourself the "transparent" images with the approch to collect the message and modify the LParam? Have you ever experienced if it really works?
If I remember correctly, native listview supports the DI_GETDRAGIMAGE message directly. It should create a drag image of the selected items automatically. However, I'm not sure custom draw will work in this case.
Because the listview's built-in drag image creation capabilities are too limited for me, I handle LVM_CREATEDRAGIMAGE and DI_GETDRAGIMAGE myself and draw the drag images myself. This is anything but trivial, but this way I have full control over the drag images.

What kind of items are you displaying in your listview? No specific? Or shell items like in Windows Explorer? If you're displaying shell items only, there's a much easier way to get drag images, even Aero drag images become trivial then.
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

Post by cordov »

Wow, that's a really ...erm... strange approach
What is the best approach? Pinvoke?
If I remember correctly, native listview supports the DI_GETDRAGIMAGE message directly. It should create a drag image of the selected items automatically. However, I'm not sure custom draw will work in this case.
I'm sure the automatic creation doesn't work for custom drawn listview
I handle LVM_CREATEDRAGIMAGE and DI_GETDRAGIMAGE myself and draw the drag images myself.
it's what i would like to do and I can't!!!!!! have you some samples? some links? Any help for the wrapper of the first post that create the bitmap?

What kind of items are you displaying in your listview?
Just a text and an image. But as I told before, I have a custom drawn listview, so the text (that at the moment is a ListviewItem with only text and no image) could be everywhere in the control, also far from its image.

Thank you very much

Luigi
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Post by TiKu »

cordov wrote:
Wow, that's a really ...erm... strange approach
What is the best approach? Pinvoke?
Probably. Writing a shell extension(!) to get drag images work would be the very last option I'd choose. It sounds like much work, not only because shell extensions often get nasty, too.
cordov wrote:
I handle LVM_CREATEDRAGIMAGE and DI_GETDRAGIMAGE myself and draw the drag images myself.
it's what i would like to do and I can't!!!!!! have you some samples? some links? Any help for the wrapper of the first post that create the bitmap?
Here's the C++ code that I'm using in ExplorerListView. It doesn't send NM_CUSTOMDRAW notifications to the parent window, so it doesn't support custom draw. But since it draws the whole drag image itself, it's easy to customize the item appearance within the drag image.

Code: Select all

BOOL ExplorerListView::CreateLegacyOLEDragImage(IListViewItemContainer* pItems, LPSHDRAGIMAGE pDragImage)
{
	ATLASSUME(pItems);
	ATLASSERT_POINTER(pDragImage, SHDRAGIMAGE);

	BOOL succeeded = FALSE;

	// use a normal legacy drag image as base
	OLE_HANDLE h = NULL;
	OLE_XPOS_PIXELS xUpperLeft = 0;
	OLE_YPOS_PIXELS yUpperLeft = 0;
	pItems->CreateDragImage(&xUpperLeft, &yUpperLeft, &h);
	if(h) {
		HIMAGELIST hImageList = (HIMAGELIST) LongToHandle(h);

		// retrieve the drag image's size
		int bitmapHeight;
		int bitmapWidth;
		::ImageList_GetIconSize(hImageList, &bitmapWidth, &bitmapHeight);
		pDragImage->sizeDragImage.cx = bitmapWidth;
		pDragImage->sizeDragImage.cy = bitmapHeight;

		WTL::CDC memoryDC;
		memoryDC.CreateCompatibleDC();
		pDragImage->hbmpDragImage = NULL;

		if(IsComctl32Version600OrNewer()) {
			// handle alpha channel

			IImageList* pImgLst = NULL;
			HMODULE hMod = ::LoadLibrary(TEXT("comctl32.dll"));
			if(hMod) {
				typedef HRESULT WINAPI HIMAGELIST_QueryInterfaceFn(HIMAGELIST, REFIID, void**);
				HIMAGELIST_QueryInterfaceFn* pfnHIMAGELIST_QueryInterface = (HIMAGELIST_QueryInterfaceFn*) GetProcAddress(hMod, "HIMAGELIST_QueryInterface");
				if(pfnHIMAGELIST_QueryInterface) {
					pfnHIMAGELIST_QueryInterface(hImageList, IID_IImageList, reinterpret_cast<void**>(&pImgLst));
				}
				::FreeLibrary(hMod);
			}
			if(!pImgLst) {
				pImgLst = reinterpret_cast<IImageList*>(hImageList);
				pImgLst->AddRef();
			}
			ATLASSUME(pImgLst);

			DWORD flags = 0;
			pImgLst->GetItemFlags(0, &flags);
			if(flags & ILIF_ALPHA) {
				// the drag image makes use of the alpha channel
				IMAGEINFO imageInfo = {0};
				::ImageList_GetImageInfo(hImageList, 0, &imageInfo);

				// fetch raw data
				BITMAPINFO bitmapInfo = {0};
				bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				bitmapInfo.bmiHeader.biWidth = pDragImage->sizeDragImage.cx;
				bitmapInfo.bmiHeader.biHeight = -pDragImage->sizeDragImage.cy;
				bitmapInfo.bmiHeader.biPlanes = 1;
				bitmapInfo.bmiHeader.biBitCount = 32;
				bitmapInfo.bmiHeader.biCompression = BI_RGB;
				LPRGBQUAD pSourceBits = (LPRGBQUAD) malloc(pDragImage->sizeDragImage.cx * pDragImage->sizeDragImage.cy * sizeof(RGBQUAD));
				::GetDIBits(memoryDC, imageInfo.hbmImage, 0, pDragImage->sizeDragImage.cy, pSourceBits, &bitmapInfo, DIB_RGB_COLORS);
				// create target bitmap
				LPRGBQUAD pDragImageBits = NULL;
				pDragImage->hbmpDragImage = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&pDragImageBits), NULL, 0);
				pDragImage->crColorKey = 0xFFFFFFFF;

				// transfer raw data
				memcpy(pDragImageBits, pSourceBits, pDragImage->sizeDragImage.cx * pDragImage->sizeDragImage.cy * 4);

				// clean up
				SECUREFREE(pSourceBits);
				::DeleteObject(imageInfo.hbmImage);
				::DeleteObject(imageInfo.hbmMask);
			}
			pImgLst->Release();
		}

		if(pDragImage->hbmpDragImage == NULL) {
			// fallback mode
			memoryDC.SetBkMode(TRANSPARENT);

			// create target bitmap
			HDC hCompatibleDC = ::GetDC(NULL);
			pDragImage->hbmpDragImage = ::CreateCompatibleBitmap(hCompatibleDC, bitmapWidth, bitmapHeight);
			::ReleaseDC(NULL, hCompatibleDC);
			HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(pDragImage->hbmpDragImage);

			// draw target bitmap
			pDragImage->crColorKey = RGB(0xF4, 0x00, 0x00);
			WTL::CBrush backroundBrush;
			backroundBrush.CreateSolidBrush(pDragImage->crColorKey);
			memoryDC.FillRect(WTL::CRect(0, 0, bitmapWidth, bitmapHeight), backroundBrush);
			::ImageList_Draw(hImageList, 0, memoryDC, 0, 0, ILD_NORMAL);

			// clean up
			memoryDC.SelectBitmap(hPreviousBitmap);
		}

		::ImageList_Destroy(hImageList);

		if(pDragImage->hbmpDragImage != NULL) {
			// retrieve the offset
			DWORD position = ::GetMessagePos();
			POINT mousePosition = {GET_X_LPARAM(position), GET_Y_LPARAM(position)};
			ScreenToClient(&mousePosition);
			if(GetExStyle() & WS_EX_LAYOUTRTL) {
				pDragImage->ptOffset.x = xUpperLeft + pDragImage->sizeDragImage.cx - mousePosition.x;
			} else {
				pDragImage->ptOffset.x = mousePosition.x - xUpperLeft;
			}
			pDragImage->ptOffset.y = mousePosition.y - yUpperLeft;

			succeeded = TRUE;
		}
	}

	return succeeded;
}

// handles DI_GETDRAGIMAGE
LRESULT ExplorerListView::OnGetDragImage(UINT /*message*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& wasHandled)
{
	BOOL succeeded = FALSE;
	if(dragDropStatus.pDraggedItems) {
		succeeded = CreateLegacyOLEDragImage(dragDropStatus.pDraggedItems, reinterpret_cast<LPSHDRAGIMAGE>(lParam));
	}

	wasHandled = succeeded;
	return succeeded;
}
You may have noticed the line

Code: Select all

pItems->CreateDragImage(&xUpperLeft, &yUpperLeft, &h);
This method creates drag images for each dragged item and combines them to one large bitmap.

Code: Select all

STDMETHODIMP ListViewItemContainer::CreateDragImage(OLE_XPOS_PIXELS* pXUpperLeft/* = NULL*/, OLE_YPOS_PIXELS* pYUpperLeft/* = NULL*/, OLE_HANDLE* phImageList/* = NULL*/)
{
	ATLASSERT_POINTER(phImageList, OLE_HANDLE);
	if(phImageList == NULL) {
		return E_POINTER;
	}

	*phImageList = NULL;
	#ifdef USE_STL
		switch(properties.items.size()) {
	#else
		//switch(properties.items.GetCount()) {
		switch(items.GetCount()) {
	#endif
		case 0:
			return S_OK;
			break;
		case 1: {
			ATLASSUME(properties.pOwnerExLvw);
			//int itemIndex = properties.pOwnerExLvw->IDToItemIndex(properties.items[0]);
			#ifdef USE_STL
				int itemIndex = properties.pOwnerExLvw->IDToItemIndex(properties.items[0]);
			#else
				int itemIndex = properties.pOwnerExLvw->IDToItemIndex(items[0]);
			#endif
			if(itemIndex != -1) {
				POINT upperLeftPoint = {0};
				*phImageList = HandleToLong(properties.pOwnerExLvw->CreateLegacyDragImage(itemIndex, &upperLeftPoint, NULL));
				if(*phImageList) {
					if(pXUpperLeft) {
						*pXUpperLeft = upperLeftPoint.x;
					}
					if(pYUpperLeft) {
						*pYUpperLeft = upperLeftPoint.y;
					}
					return S_OK;
				}
			}
			break;
		}
		default: {
			// create a large drag image out of small drag images
			ATLASSUME(properties.pOwnerExLvw);

			BOOL use32BPPImage = properties.pOwnerExLvw->IsComctl32Version600OrNewer();

			// calculate the bitmap's required size and collect each item's imagelist
			#ifdef USE_STL
				std::vector< HIMAGELIST > imageLists;
				std::vector< RECT > itemBoundingRects;
			#else
				CAtlArray< HIMAGELIST > imageLists;
				CAtlArray< RECT > itemBoundingRects;
			#endif
			POINT upperLeftPoint = {0};
			WTL::CRect boundingRect;
			#ifdef USE_STL
				for(std::vector< LONG >::iterator iter = properties.items.begin(); iter != properties.items.end(); ++iter) {
					int itemIndex = properties.pOwnerExLvw->IDToItemIndex(*iter);
			#else
				//for(size_t i = 0; i < properties.items.GetCount(); ++i) {
				for(size_t i = 0; i < items.GetCount(); ++i) {
					//int itemIndex = properties.pOwnerExLvw->IDToItemIndex(properties.items[i]);
					int itemIndex = properties.pOwnerExLvw->IDToItemIndex(items[i]);
			#endif
				if(itemIndex != -1) {
					// NOTE: Windows skips items outside the client area to improve performance. We don't.
					POINT pt = {0};
					RECT itemBoundingRect = {0};
					HIMAGELIST hImageList = properties.pOwnerExLvw->CreateLegacyDragImage(itemIndex, &pt, &itemBoundingRect);
					boundingRect.UnionRect(&boundingRect, &itemBoundingRect);

					#ifdef USE_STL
						if(imageLists.size() == 0) {
					#else
						if(imageLists.GetCount() == 0) {
					#endif
						upperLeftPoint = pt;
					} else {
						upperLeftPoint.x = min(upperLeftPoint.x, pt.x);
						upperLeftPoint.y = min(upperLeftPoint.y, pt.y);
					}
					#ifdef USE_STL
						imageLists.push_back(hImageList);
						itemBoundingRects.push_back(itemBoundingRect);
					#else
						imageLists.Add(hImageList);
						itemBoundingRects.Add(itemBoundingRect);
					#endif
				}
			}
			WTL::CRect dragImageRect(0, 0, boundingRect.Width(), boundingRect.Height());

			// setup the DCs we'll draw into
			HDC hCompatibleDC = ::GetDC(NULL);
			WTL::CDC memoryDC;
			memoryDC.CreateCompatibleDC(hCompatibleDC);
			WTL::CDC maskMemoryDC;
			maskMemoryDC.CreateCompatibleDC(hCompatibleDC);

			// create the bitmap and its mask
			WTL::CBitmap dragImage;
			LPRGBQUAD pDragImageBits = NULL;
			if(use32BPPImage) {
				BITMAPINFO bitmapInfo = {0};
				bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				bitmapInfo.bmiHeader.biWidth = dragImageRect.Width();
				bitmapInfo.bmiHeader.biHeight = -dragImageRect.Height();
				bitmapInfo.bmiHeader.biPlanes = 1;
				bitmapInfo.bmiHeader.biBitCount = 32;
				bitmapInfo.bmiHeader.biCompression = BI_RGB;

				dragImage.CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&pDragImageBits), NULL, 0);
			} else {
				dragImage.CreateCompatibleBitmap(hCompatibleDC, dragImageRect.Width(), dragImageRect.Height());
			}
			HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(dragImage);
			memoryDC.FillRect(&dragImageRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
			WTL::CBitmap dragImageMask;
			dragImageMask.CreateBitmap(dragImageRect.Width(), dragImageRect.Height(), 1, 1, NULL);
			HBITMAP hPreviousBitmapMask = maskMemoryDC.SelectBitmap(dragImageMask);
			maskMemoryDC.FillRect(&dragImageRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));

			// draw each single drag image into our bitmap
			BOOL rightToLeft = FALSE;
			HWND hWndLvw = properties.GetExLvwHWnd();
			if(::IsWindow(hWndLvw)) {
				rightToLeft = ((CWindow(hWndLvw).GetExStyle() & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL);
			}
			#ifdef USE_STL
				for(size_t i = 0; i < imageLists.size(); ++i) {
			#else
				for(size_t i = 0; i < imageLists.GetCount(); ++i) {
			#endif
				if(rightToLeft) {
					::ImageList_Draw(imageLists[i], 0, memoryDC, boundingRect.right - itemBoundingRects[i].right, itemBoundingRects[i].top - boundingRect.top, ILD_NORMAL);
					::ImageList_Draw(imageLists[i], 0, maskMemoryDC, boundingRect.right - itemBoundingRects[i].right, itemBoundingRects[i].top - boundingRect.top, ILD_MASK);
				} else {
					::ImageList_Draw(imageLists[i], 0, memoryDC, itemBoundingRects[i].left - boundingRect.left, itemBoundingRects[i].top - boundingRect.top, ILD_NORMAL);
					::ImageList_Draw(imageLists[i], 0, maskMemoryDC, itemBoundingRects[i].left - boundingRect.left, itemBoundingRects[i].top - boundingRect.top, ILD_MASK);
				}

				::ImageList_Destroy(imageLists[i]);
			}

			// clean up
			#ifdef USE_STL
				imageLists.clear();
				itemBoundingRects.clear();
			#else
				imageLists.RemoveAll();
				itemBoundingRects.RemoveAll();
			#endif
			memoryDC.SelectBitmap(hPreviousBitmap);
			maskMemoryDC.SelectBitmap(hPreviousBitmapMask);
			::ReleaseDC(NULL, hCompatibleDC);

			// create the imagelist
			HIMAGELIST hImageList = ::ImageList_Create(dragImageRect.Width(), dragImageRect.Height(), (use32BPPImage ? ILC_COLOR32 : ILC_COLOR24) | ILC_MASK, 1, 0);
			::ImageList_SetBkColor(hImageList, CLR_NONE);
			::ImageList_Add(hImageList, dragImage, dragImageMask);
			*phImageList = HandleToLong((HIMAGELIST) hImageList);

			if(*phImageList) {
				if(pXUpperLeft) {
					*pXUpperLeft = upperLeftPoint.x;
				}
				if(pYUpperLeft) {
					*pYUpperLeft = upperLeftPoint.y;
				}
				return S_OK;
			}
			break;
		}
	}

	return E_FAIL;
}
The last method missing is CreateLegacyDragImage, which creates a drag image for a single item. This is the one that should be edited to customize the appearance of the drag image.

Code: Select all

HIMAGELIST ExplorerListView::CreateLegacyDragImage(int itemIndex, POINT* pUpperLeftPoint, RECT* pBoundingRectangle)
{
	/********************************************************************************************************
	 * Known problems:                                                                                      *
	 * - We assume that the selection background goes over icon and label for all themes.                   *
	 * - We use hardcoded margins.                                                                          *
	 * - Extended Tile View is not fully supported (displaying the details in two columns doesn't work).    *
	 ********************************************************************************************************/

	// retrieve item details
	LVITEM item = {0};
	item.iItem = itemIndex;
	item.cchTextMax = MAX_ITEMTEXTLENGTH;
	LPTSTR pItemTextBuffer = (LPTSTR) malloc((item.cchTextMax + 1) * sizeof(TCHAR));
	item.pszText = pItemTextBuffer;
	item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_TEXT;
	item.stateMask = LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK;
	SendMessage(LVM_GETITEM, 0, (LPARAM) &item);
	if(item.pszText == NULL) {
		item.pszText = TEXT("");
	}
	BOOL itemIsSelected = ((item.state & LVIS_SELECTED) == LVIS_SELECTED);
	BOOL itemIsFocused = ((item.state & LVIS_FOCUSED) == LVIS_FOCUSED);
	int iconToDraw = item.iImage;
	CComPtr< IListViewItem > pItem = NULL;
	VARIANT vTileViewColumns;
	::VariantInit(&vTileViewColumns);
	int lines = 0;
	int maxDetailLinesBySettings = 0;
	int numberOfMainTextLines = 0;
	int lineHeight = 0;
	LVTILEVIEWINFO tileViewInfo = {0};
	int columnCount = 0;
	RECT* pSubItemImageRectangles = NULL;
	BOOL* pSubItemImageHasAlpha = NULL;

	// retrieve window details
	BOOL hasFocus = (::GetFocus() == m_hWnd);
	DWORD style = GetExStyle();
	DWORD textDrawStyle = DT_EDITCONTROL | DT_NOPREFIX;
	if(style & WS_EX_RTLREADING) {
		textDrawStyle |= DT_RTLREADING;
	}
	BOOL layoutRTL = ((style & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL);
	DWORD extendedStyle = (DWORD) SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
	BOOL fullRowSelect = ((extendedStyle & LVS_EX_FULLROWSELECT) == LVS_EX_FULLROWSELECT);
	ViewConstants currentView = vIcons;
	get_View(&currentView);
	HIMAGELIST hSourceImageList = NULL;
	switch(currentView) {
		case vDetails:
			hSourceImageList = cachedSettings.hSmallImageList;
			textDrawStyle |= DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
			break;
		case vIcons:
			hSourceImageList = cachedSettings.hLargeImageList;
			textDrawStyle |= DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS;
			break;
		case vList:
			hSourceImageList = cachedSettings.hSmallImageList;
			textDrawStyle |= DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
			break;
		case vSmallIcons:
			hSourceImageList = cachedSettings.hSmallImageList;
			textDrawStyle |= DT_SINGLELINE;
			break;
		case vTiles:
			hSourceImageList = cachedSettings.hExtraLargeImageList;
			textDrawStyle |= DT_END_ELLIPSIS;
			break;
		case vExtendedTiles:
			hSourceImageList = cachedSettings.hExtraLargeImageList;
			textDrawStyle |= DT_END_ELLIPSIS;
			break;
	}
	SIZE imageSize = {0};
	BOOL subItemImages = FALSE;
	if(hSourceImageList) {
		if(currentView == vDetails) {
			subItemImages = ((extendedStyle & LVS_EX_SUBITEMIMAGES) == LVS_EX_SUBITEMIMAGES);
		}

		::ImageList_GetIconSize(hSourceImageList, (int*) &imageSize.cx, (int*) &imageSize.cy);
	}

	// create the DCs we'll draw into
	HDC hCompatibleDC = GetDC();
	WTL::CDC memoryDC;
	memoryDC.CreateCompatibleDC(hCompatibleDC);
	WTL::CDC maskMemoryDC;
	maskMemoryDC.CreateCompatibleDC(hCompatibleDC);

	WTL::CFontHandle font = (HFONT) SendMessage(WM_GETFONT, 0, 0);
	HFONT hPreviousFont = NULL;
	if(!font.IsNull()) {
		hPreviousFont = memoryDC.SelectFont(font);
	}

	// prepare themed item drawing
	CTheme themingEngine;
	BOOL themedListItems = FALSE;
	int themeState = LISS_NORMAL;
	if(itemIsSelected) {
		if(hasFocus) {
			themeState = LISS_SELECTED;
		} else {
			themeState = LISS_SELECTEDNOTFOCUS;
		}
	}
	if(flags.usingThemes) {
		themingEngine.OpenThemeData(*this, VSCLASS_LISTVIEW);
		/* We use LISS_SELECTED here, because it's more likely this one is defined and we don't want a mixture
		   of themed and non-themed items in drag images. What we're doing with LISS_NORMAL should work
		   regardless whether LISS_NORMAL is defined. */
		themedListItems = themingEngine.IsThemePartDefined(LVP_LISTITEM, LISS_SELECTED/*themeState*/);
		if(themedListItems) {
			if(currentView == vSmallIcons) {
				textDrawStyle |= DT_VCENTER;
			}
		}
	}

	// calculate the bounding rectangles of the various item parts
	WTL::CRect itemBoundingRect;
	WTL::CRect selectionBoundingRect;
	WTL::CRect focusRect;
	WTL::CRect labelBoundingRect;
	WTL::CRect iconAreaBoundingRect;
	WTL::CRect iconBoundingRect;

	labelBoundingRect.left = LVIR_LABEL;
	SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &labelBoundingRect);
	selectionBoundingRect.left = LVIR_SELECTBOUNDS;
	SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &selectionBoundingRect);
	itemBoundingRect = selectionBoundingRect;
	if(!themedListItems) {
		if(currentView == vList) {
			// the width of labelBoundingRect may be wrong
			if(lstrlen(item.pszText) > 0) {
				WTL::CRect rc = labelBoundingRect;
				memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle | DT_CALCRECT);
				// TODO: Don't use hard-coded margins
				labelBoundingRect.right = labelBoundingRect.left + rc.Width() + 4;
			}
		}
	}
	if((currentView == vTiles) || (currentView == vExtendedTiles)) {
		// the height of labelBoundingRect may be wrong

		// calculate the line height
		WTL::CRect rc;
		rc.SetRect(0, 0, 30, 200000);
		LPTSTR pDummy = TEXT("A\r\nB");
		// TODO: DrawThemeText() doesn't set the rectangle's height
		/*if(themedListItems) {
			CT2W converter(pDummy);
			LPWSTR pLabelText = converter;
			themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK | DT_CALCRECT, 0, &rc);
		} else {*/
			memoryDC.DrawText(pDummy, lstrlen(pDummy), &rc, textDrawStyle | DT_WORDBREAK | DT_CALCRECT);
		//}
		lineHeight = rc.Height() / 2;

		// retrieve the maximum number of lines
		tileViewInfo.cbSize = sizeof(LVTILEVIEWINFO);
		tileViewInfo.dwMask = LVTVIM_COLUMNS | LVTVIM_TILESIZE;
		SendMessage(LVM_GETTILEVIEWINFO, 0, (LPARAM) &tileViewInfo);
		// retrieve the number of sub-items to display
		pItem = ClassFactory::InitListItem(itemIndex, this, false);
		ATLASSUME(pItem);
		ATLVERIFY(SUCCEEDED(pItem->get_TileViewColumns(&vTileViewColumns)));
		ATLASSERT(vTileViewColumns.vt & VT_ARRAY);
		maxDetailLinesBySettings = 0;
		if(vTileViewColumns.parray) {
			LONG l = 0;
			::SafeArrayGetLBound(vTileViewColumns.parray, 1, &l);
			LONG u = 0;
			::SafeArrayGetUBound(vTileViewColumns.parray, 1, &u);
			maxDetailLinesBySettings = min(u - l + 1, tileViewInfo.cLines);
		}

		// count the lines
		numberOfMainTextLines = 1;
		lines = 1;
		CAtlString itemText = item.pszText;
		// the item's main text may consist of up to two lines, so check this
		if(itemText.Find(TEXT("\r\n")) != -1) {
			++lines;
			++numberOfMainTextLines;
		}
		int maxLines = min(maxDetailLinesBySettings + numberOfMainTextLines, tileViewInfo.cLines + 1);
		if(currentView == vExtendedTiles) {
			int maxLinesByHeight = labelBoundingRect.Height() / lineHeight;
			maxLines = min(maxLines, maxLinesByHeight);
		}

		LVITEM subItem = {0};
		subItem.cchTextMax = MAX_ITEMTEXTLENGTH;
		LPTSTR pSubItemTextBuffer = (LPTSTR) malloc((subItem.cchTextMax + 1) * sizeof(TCHAR));
		for(LONG line = 0; line < maxDetailLinesBySettings; ++line) {
			subItem.pszText = pSubItemTextBuffer;
			ATLVERIFY(SUCCEEDED(::SafeArrayGetElement(vTileViewColumns.parray, &line, &subItem.iSubItem)));
			SendMessage(LVM_GETITEMTEXT, (WPARAM) itemIndex, (LPARAM) &subItem);
			if(subItem.pszText) {
				if(lstrlen(subItem.pszText) > 0) {
					++lines;
				}
			}
		}
		lines = min(lines, maxLines);

		// calculate the real height of labelBoundingRect
		if((currentView == vExtendedTiles) && !themedListItems) {
			labelBoundingRect.bottom = selectionBoundingRect.bottom;
		} else {
			labelBoundingRect.bottom = labelBoundingRect.top + lines * lineHeight;
		}

		if(!themedListItems && (currentView != vExtendedTiles)) {
			if(((tileViewInfo.dwFlags & LVTVIF_FIXEDWIDTH) == 0) && !fullRowSelect) {
				// the width may be wrong, too
				int remainingLines = lines;
				WTL::CRect textAreaRect;
				rc = labelBoundingRect;
				rc.bottom = rc.top + lineHeight;
				if((numberOfMainTextLines == 1) || (remainingLines == 1)) {
					memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE | DT_CALCRECT);
					textAreaRect.UnionRect(&textAreaRect, &rc);
					--remainingLines;
				} else {
					int i = itemText.Find(TEXT("\r\n"));
					ATLASSERT(i > 0);
					CAtlString firstLine = itemText.Left(i);
					CAtlString secondLine = itemText.Mid(i + 2);
					memoryDC.DrawText(firstLine, lstrlen(firstLine), &rc, textDrawStyle | DT_WORDBREAK | DT_CALCRECT);
					textAreaRect.UnionRect(&textAreaRect, &rc);
					--remainingLines;
					rc = labelBoundingRect;
					rc.top = labelBoundingRect.top + lineHeight;
					rc.bottom = rc.top + lineHeight;
					memoryDC.DrawText(secondLine, lstrlen(secondLine), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE | DT_CALCRECT);
					textAreaRect.UnionRect(&textAreaRect, &rc);
					--remainingLines;
				}
				int numberOfDetailLines = min(remainingLines, maxDetailLinesBySettings);
				int nonEmptyLine = 0;
				for(LONG line = 0; nonEmptyLine < numberOfDetailLines; ++line) {
					subItem.pszText = pSubItemTextBuffer;
					ATLVERIFY(SUCCEEDED(::SafeArrayGetElement(vTileViewColumns.parray, &line, &subItem.iSubItem)));
					SendMessage(LVM_GETITEMTEXT, (WPARAM) itemIndex, (LPARAM) &subItem);
					if(subItem.pszText) {
						if(lstrlen(subItem.pszText) > 0) {
							rc = labelBoundingRect;
							rc.top = labelBoundingRect.top + (lines - remainingLines) * lineHeight;
							rc.bottom = rc.top + lineHeight;

							memoryDC.DrawText(subItem.pszText, lstrlen(subItem.pszText), &rc, textDrawStyle | DT_SINGLELINE | DT_CALCRECT);
							textAreaRect.UnionRect(&textAreaRect, &rc);
							--remainingLines;
							++nonEmptyLine;
						}
					}
				}

				labelBoundingRect.right = textAreaRect.right;
			}
		}

		SECUREFREE(pSubItemTextBuffer);
	}
	if((currentView != vIcons) && (currentView != vExtendedTiles)) {
		// center the label rectangle vertically
		int cy = labelBoundingRect.Height();
		labelBoundingRect.top = itemBoundingRect.top + (itemBoundingRect.Height() - cy) / 2;
		labelBoundingRect.bottom = labelBoundingRect.top + cy;
	}
	if(themedListItems) {
		if(currentView == vDetails) {
			if(!fullRowSelect) {
				// TODO: Don't use hard-coded margins
				selectionBoundingRect.right -= 2;
				labelBoundingRect.right = min(labelBoundingRect.right, selectionBoundingRect.right);
			}
		}
	} else {
		// selectionBoundingRect includes the icon, so correct it
		if(currentView == vDetails) {
			// labelBoundingRect covers the entire first column and nothing more
			selectionBoundingRect.left = labelBoundingRect.left;
			if(!fullRowSelect) {
				labelBoundingRect.right = selectionBoundingRect.right;
			}
		} else {
			selectionBoundingRect = labelBoundingRect;
		}
	}
	if(hSourceImageList) {
		iconAreaBoundingRect.left = LVIR_ICON;
		SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &iconAreaBoundingRect);
		if(themedListItems) {
			if(currentView == vSmallIcons) {
				// TODO: Don't use hard-coded margins
				iconAreaBoundingRect.left += 4;
			}
		}
		if((currentView == vTiles) || (currentView == vExtendedTiles)) {
			if(iconAreaBoundingRect.Height() < labelBoundingRect.Height()) {
				iconAreaBoundingRect.top = labelBoundingRect.top;
				iconAreaBoundingRect.bottom = labelBoundingRect.bottom;
			}
			// TODO: Don't use hard-coded margins
			iconAreaBoundingRect.right = labelBoundingRect.left - 4;
			iconAreaBoundingRect.left = iconAreaBoundingRect.right - imageSize.cx;
		}
		if(currentView != vIcons) {
			// center the icon area rectangle vertically
			int cy = iconAreaBoundingRect.Height();
			iconAreaBoundingRect.top = itemBoundingRect.top + (itemBoundingRect.Height() - cy) / 2;
			iconAreaBoundingRect.bottom = iconAreaBoundingRect.top + cy;
		}
		iconBoundingRect = iconAreaBoundingRect;
		if(currentView == vIcons) {
			// center the icon horizontally
			// TODO: Don't use hard-coded margins
			iconBoundingRect.top += 2;
			iconBoundingRect.left += (iconAreaBoundingRect.Width() - imageSize.cx) / 2;
			iconBoundingRect.bottom = iconBoundingRect.top + imageSize.cy;
			iconBoundingRect.right = iconBoundingRect.left + imageSize.cx;
		} else {
			// center the icon vertically
			iconBoundingRect.top += (iconAreaBoundingRect.Height() - imageSize.cy) / 2;
			iconBoundingRect.bottom = iconBoundingRect.top + imageSize.cy;
			iconBoundingRect.right = iconBoundingRect.left + imageSize.cx;
		}
	}
	itemBoundingRect.left = LVIR_BOUNDS;
	SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &itemBoundingRect);
	if(hSourceImageList) {
		itemBoundingRect.UnionRect(&itemBoundingRect, &iconAreaBoundingRect);
	}
	itemBoundingRect.UnionRect(&itemBoundingRect, &labelBoundingRect);
	focusRect = selectionBoundingRect;
	if(pBoundingRectangle) {
		*pBoundingRectangle = itemBoundingRect;
	}

	// calculate drag image size and upper-left corner
	SIZE dragImageSize = {0};
	if(pUpperLeftPoint) {
		pUpperLeftPoint->x = itemBoundingRect.left;
		pUpperLeftPoint->y = itemBoundingRect.top;
	}
	dragImageSize.cx = itemBoundingRect.Width();
	dragImageSize.cy = itemBoundingRect.Height();

	// offset RECTs
	SIZE offset = {0};
	offset.cx = itemBoundingRect.left;
	offset.cy = itemBoundingRect.top;
	labelBoundingRect.OffsetRect(-offset.cx, -offset.cy);
	iconAreaBoundingRect.OffsetRect(-offset.cx, -offset.cy);
	iconBoundingRect.OffsetRect(-offset.cx, -offset.cy);
	selectionBoundingRect.OffsetRect(-offset.cx, -offset.cy);
	itemBoundingRect.OffsetRect(-offset.cx, -offset.cy);

	// setup the DCs we'll draw into
	if(itemIsSelected) {
		memoryDC.SetBkColor(::GetSysColor((hasFocus ? COLOR_HIGHLIGHT : COLOR_BTNFACE)));
		memoryDC.SetTextColor(::GetSysColor((hasFocus ? COLOR_HIGHLIGHTTEXT : COLOR_BTNTEXT)));
	} else {
		memoryDC.SetBkColor(::GetSysColor(COLOR_WINDOW));
		memoryDC.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
		memoryDC.SetBkMode(TRANSPARENT);
	}

	// create drag image bitmap
	/* NOTE: We prefer creating 32bpp drag images, because this improves performance of
	         ListViewItemContainer::CreateDragImage(). */
	BOOL doAlphaChannelProcessing = IsComctl32Version600OrNewer();
	BITMAPINFO bitmapInfo = {0};
	if(doAlphaChannelProcessing) {
		bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bitmapInfo.bmiHeader.biWidth = dragImageSize.cx;
		bitmapInfo.bmiHeader.biHeight = -dragImageSize.cy;
		bitmapInfo.bmiHeader.biPlanes = 1;
		bitmapInfo.bmiHeader.biBitCount = 32;
		bitmapInfo.bmiHeader.biCompression = BI_RGB;
	}
	WTL::CBitmap dragImage;
	LPRGBQUAD pDragImageBits = NULL;
	if(doAlphaChannelProcessing) {
		dragImage.CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&pDragImageBits), NULL, 0);
	} else {
		dragImage.CreateCompatibleBitmap(hCompatibleDC, dragImageSize.cx, dragImageSize.cy);
	}
	HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(dragImage);
	WTL::CBitmap dragImageMask;
	dragImageMask.CreateBitmap(dragImageSize.cx, dragImageSize.cy, 1, 1, NULL);
	HBITMAP hPreviousBitmapMask = maskMemoryDC.SelectBitmap(dragImageMask);

	// initialize the bitmap
	if(themedListItems) {
		// we need a transparent background
		LPRGBQUAD pPixel = pDragImageBits;
		for(int y = 0; y < dragImageSize.cy; ++y) {
			for(int x = 0; x < dragImageSize.cx; ++x, ++pPixel) {
				pPixel->rgbRed = 0xFF;
				pPixel->rgbGreen = 0xFF;
				pPixel->rgbBlue = 0xFF;
				pPixel->rgbReserved = 0x00;
			}
		}
	} else {
		memoryDC.FillRect(&itemBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
	}
	maskMemoryDC.FillRect(&itemBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));

	// draw the selection area's background
	if(itemIsSelected) {
		if(themedListItems) {
			themingEngine.DrawThemeBackground(memoryDC, LVP_LISTITEM, themeState, &selectionBoundingRect, NULL);
		} else {
			memoryDC.FillRect(&selectionBoundingRect, (hasFocus ? COLOR_HIGHLIGHT : COLOR_BTNFACE));
		}
		maskMemoryDC.FillRect(&selectionBoundingRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
	}

	// draw the icon
	if(hSourceImageList) {
		::ImageList_DrawEx(hSourceImageList, iconToDraw, memoryDC, iconBoundingRect.left, iconBoundingRect.top, imageSize.cx, imageSize.cy, CLR_NONE, CLR_NONE, ILD_NORMAL | (item.state & LVIS_OVERLAYMASK));
		if(itemIsSelected && themedListItems) {
			maskMemoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
		} else {
			::ImageList_Draw(hSourceImageList, iconToDraw, maskMemoryDC, iconBoundingRect.left, iconBoundingRect.top, ILD_MASK | (item.state & LVIS_OVERLAYMASK));
		}
	}

	// draw the text
	WTL::CRect rc = labelBoundingRect;
	if((currentView == vTiles) || (currentView == vExtendedTiles)) {
		int remainingLines = lines;
		rc.bottom = rc.top + lineHeight;
		if((numberOfMainTextLines == 1) || (remainingLines == 1)) {
			if(themedListItems) {
				CT2W converter(item.pszText);
				LPWSTR pLabelText = converter;
				themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK | DT_SINGLELINE, 0, &rc);
			} else {
				memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE);
			}
			--remainingLines;
		} else {
			CAtlString itemText = item.pszText;
			int i = itemText.Find(TEXT("\r\n"));
			ATLASSERT(i > 0);
			CAtlString firstLine = itemText.Left(i);
			CAtlString secondLine = itemText.Mid(i + 2);
			if(themedListItems) {
				CT2W converter(firstLine);
				LPWSTR pLabelText = converter;
				themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK, 0, &rc);
			} else {
				memoryDC.DrawText(firstLine, lstrlen(firstLine), &rc, textDrawStyle | DT_WORDBREAK);
			}
			--remainingLines;
			if(!itemIsSelected) {
				COLORREF bkColor = memoryDC.GetBkColor();
				for(int y = rc.top; y <= rc.bottom; ++y) {
					for(int x = rc.left; x <= rc.right; ++x) {
						if(memoryDC.GetPixel(x, y) != bkColor) {
							maskMemoryDC.SetPixelV(x, y, 0x00000000);
						}
					}
				}
			}
			rc = labelBoundingRect;
			rc.top = labelBoundingRect.top + lineHeight;
			rc.bottom = rc.top + lineHeight;
			if(themedListItems) {
				CT2W converter(secondLine);
				LPWSTR pLabelText = converter;
				themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK | DT_SINGLELINE, 0, &rc);
			} else {
				memoryDC.DrawText(secondLine, lstrlen(secondLine), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE);
			}
			--remainingLines;
		}
		if(!itemIsSelected) {
			COLORREF bkColor = memoryDC.GetBkColor();
			for(int y = rc.top; y <= rc.bottom; ++y) {
				for(int x = rc.left; x <= rc.right; ++x) {
					if(memoryDC.GetPixel(x, y) != bkColor) {
						maskMemoryDC.SetPixelV(x, y, 0x00000000);
					}
				}
			}
		}
		LVITEM subItem = {0};
		subItem.cchTextMax = MAX_ITEMTEXTLENGTH;
		LPTSTR pSubItemTextBuffer = (LPTSTR) malloc((subItem.cchTextMax + 1) * sizeof(TCHAR));
		int numberOfDetailLines = min(remainingLines, maxDetailLinesBySettings);
		int nonEmptyLine = 0;
		for(LONG line = 0; nonEmptyLine < numberOfDetailLines; ++line) {
			subItem.pszText = pSubItemTextBuffer;
			ATLVERIFY(SUCCEEDED(::SafeArrayGetElement(vTileViewColumns.parray, &line, &subItem.iSubItem)));
			SendMessage(LVM_GETITEMTEXT, (WPARAM) itemIndex, (LPARAM) &subItem);
			if(subItem.pszText) {
				if(lstrlen(subItem.pszText) > 0) {
					rc = labelBoundingRect;
					rc.top = labelBoundingRect.top + (lines - remainingLines) * lineHeight;
					rc.bottom = rc.top + lineHeight;

					if(themedListItems) {
						CT2W converter(subItem.pszText);
						LPWSTR pLabelText = converter;
						themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_SINGLELINE, 0, &rc);
					} else {
						memoryDC.DrawText(subItem.pszText, lstrlen(subItem.pszText), &rc, textDrawStyle | DT_SINGLELINE);
					}
					--remainingLines;
					++nonEmptyLine;
					if(!itemIsSelected) {
						COLORREF bkColor = memoryDC.GetBkColor();
						for(int y = rc.top; y <= rc.bottom; ++y) {
							for(int x = rc.left; x <= rc.right; ++x) {
								if(memoryDC.GetPixel(x, y) != bkColor) {
									maskMemoryDC.SetPixelV(x, y, 0x00000000);
								}
							}
						}
					}
				}
			}
		}
		SECUREFREE(pSubItemTextBuffer);
	} else {
		if(currentView == vDetails) {
			if(layoutRTL) {
				// TODO: Don't use hard-coded margins
				rc.OffsetRect(1, 0);
			}
		}
		// TODO: Don't use hard-coded margins
		rc.InflateRect(-2, 0);
		if(themedListItems) {
			CT2W converter(item.pszText);
			LPWSTR pLabelText = converter;
			themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle, 0, &rc);
		} else {
			memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle);
		}
		if(!itemIsSelected) {
			COLORREF bkColor = memoryDC.GetBkColor();
			for(int y = rc.top; y <= rc.bottom; ++y) {
				for(int x = rc.left; x <= rc.right; ++x) {
					if(memoryDC.GetPixel(x, y) != bkColor) {
						maskMemoryDC.SetPixelV(x, y, 0x00000000);
					}
				}
			}
		}

		if((currentView == vDetails) && fullRowSelect) {
			columnCount = (int) containedSysHeader32.SendMessage(HDM_GETITEMCOUNT, 0, 0);
			pSubItemImageRectangles = new RECT[columnCount - 1];
			pSubItemImageHasAlpha = new BOOL[columnCount - 1];

			// draw the sub-items
			LVITEM subItem = {0};
			subItem.iItem = itemIndex;
			subItem.cchTextMax = MAX_ITEMTEXTLENGTH;
			LPTSTR pSubItemTextBuffer = (LPTSTR) malloc((subItem.cchTextMax + 1) * sizeof(TCHAR));
			subItem.mask = LVIF_STATE | LVIF_TEXT;
			if(subItemImages) {
				subItem.mask |= LVIF_IMAGE;
			}
			subItem.stateMask = LVIS_OVERLAYMASK;
			LVCOLUMN column = {0};
			column.mask = LVCF_FMT;

			HMODULE hMod = ::LoadLibrary(TEXT("comctl32.dll"));
			typedef HRESULT WINAPI HIMAGELIST_QueryInterfaceFn(HIMAGELIST, REFIID, void**);
			HIMAGELIST_QueryInterfaceFn* pfnHIMAGELIST_QueryInterface = NULL;
			if(hMod) {
				pfnHIMAGELIST_QueryInterface = (HIMAGELIST_QueryInterfaceFn*) GetProcAddress(hMod, "HIMAGELIST_QueryInterface");
			}
			for(int subItemIndex = 1; subItemIndex < columnCount; ++subItemIndex) {
				// collect all the data we'll need
				WTL::CRect subItemRect;
				subItemRect.top = subItemIndex;
				subItemRect.left = LVIR_BOUNDS;
				SendMessage(LVM_GETSUBITEMRECT, (WPARAM) itemIndex, (LPARAM) &subItemRect);
				subItemRect.OffsetRect(-offset.cx, -offset.cy);

				subItem.pszText = pSubItemTextBuffer;
				subItem.iSubItem = subItemIndex;
				SendMessage(LVM_GETITEM, 0, (LPARAM) &subItem);
				if(subItem.pszText == NULL) {
					continue;
				}
				SendMessage(LVM_GETCOLUMN, (WPARAM) subItemIndex, (LPARAM) &column);
				switch(column.fmt & LVCFMT_JUSTIFYMASK) {
					case LVCFMT_LEFT:
						textDrawStyle &= ~(DT_CENTER | DT_RIGHT);
						textDrawStyle |= DT_LEFT;
						break;
					case LVCFMT_CENTER:
						textDrawStyle &= ~(DT_LEFT | DT_RIGHT);
						textDrawStyle |= DT_CENTER;
						break;
					case LVCFMT_RIGHT:
						textDrawStyle &= ~(DT_LEFT | DT_CENTER);
						textDrawStyle |= DT_RIGHT;
						break;
				}

				// draw the icon
				if(subItemImages) {
					iconBoundingRect = subItemRect;
					// TODO: Don't use hard-coded margins
					iconBoundingRect.right = iconBoundingRect.left + imageSize.cx + 2;

					if(IsComctl32Version600OrNewer()) {
						pSubItemImageRectangles[subItemIndex - 1] = iconBoundingRect;
						IImageList* pImgLst = NULL;
						if(pfnHIMAGELIST_QueryInterface) {
							pfnHIMAGELIST_QueryInterface(hSourceImageList, IID_IImageList, reinterpret_cast<void**>(&pImgLst));
						} else {
							pImgLst = reinterpret_cast<IImageList*>(hSourceImageList);
							pImgLst->AddRef();
						}
						ATLASSUME(pImgLst);

						DWORD flags = 0;
						pImgLst->GetItemFlags(subItem.iImage, &flags);
						pImgLst->Release();
						pSubItemImageHasAlpha[subItemIndex - 1] = ((flags & ILIF_ALPHA) == ILIF_ALPHA);
					}

					// background
					if(!themedListItems) {
						memoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
					}
					maskMemoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));

					// icon
					::ImageList_DrawEx(hSourceImageList, subItem.iImage, memoryDC, iconBoundingRect.left, iconBoundingRect.top, imageSize.cx, imageSize.cy, CLR_NONE, CLR_NONE, ILD_NORMAL | (subItem.state & LVIS_OVERLAYMASK));
					if(itemIsSelected && themedListItems) {
						maskMemoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
					} else {
						::ImageList_Draw(hSourceImageList, subItem.iImage, maskMemoryDC, iconBoundingRect.left, iconBoundingRect.top, ILD_MASK | (subItem.state & LVIS_OVERLAYMASK));
					}
					// TODO: Don't use hard-coded margins
					subItemRect.left = iconBoundingRect.right;
				}

				// draw the text
				if(layoutRTL) {
					// TODO: Don't use hard-coded margins
					subItemRect.OffsetRect(1, 0);
				}
				// TODO: Don't use hard-coded margins
				subItemRect.InflateRect(-2, 0);
				switch(column.fmt & LVCFMT_JUSTIFYMASK) {
					case LVCFMT_LEFT:
						// TODO: Don't use hard-coded margins
						subItemRect.left += 4;
						break;
					case LVCFMT_RIGHT:
						// TODO: Don't use hard-coded margins
						subItemRect.right -= 4;
						break;
				}
				if(themedListItems) {
					CT2W converter(subItem.pszText);
					LPWSTR pLabelText = converter;
					themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle, 0, &subItemRect);
				} else {
					memoryDC.DrawText(subItem.pszText, lstrlen(subItem.pszText), &subItemRect, textDrawStyle);
				}
				if(!itemIsSelected) {
					COLORREF bkColor = memoryDC.GetBkColor();
					for(int y = subItemRect.top; y <= subItemRect.bottom; ++y) {
						for(int x = subItemRect.left; x <= subItemRect.right; ++x) {
							if(memoryDC.GetPixel(x, y) != bkColor) {
								maskMemoryDC.SetPixelV(x, y, 0x00000000);
							}
						}
					}
				}
			}
			if(hMod) {
				::FreeLibrary(hMod);
			}

			SECUREFREE(pSubItemTextBuffer);
		}
	}

	if(!themedListItems) {
		// draw the focus rectangle
		if(hasFocus && itemIsFocused) {
			if((SendMessage(WM_QUERYUISTATE, 0, 0) & UISF_HIDEFOCUS) == 0) {
				memoryDC.DrawFocusRect(&focusRect);
				maskMemoryDC.FrameRect(&focusRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
			}
		}
	}

	if(doAlphaChannelProcessing) {
		// correct the alpha channel
		LPRGBQUAD pPixel = pDragImageBits;
		POINT pt;
		for(pt.y = 0; pt.y < dragImageSize.cy; ++pt.y) {
			for(pt.x = 0; pt.x < dragImageSize.cx; ++pt.x, ++pPixel) {
				if(layoutRTL) {
					// we're working on raw data, so we've to handle WS_EX_LAYOUTRTL ourselves
					POINT pt2 = pt;
					pt2.x = dragImageSize.cx - pt.x - 1;
					if(maskMemoryDC.GetPixel(pt2.x, pt2.y) == 0x00000000) {
						if(themedListItems) {
							if(itemIsSelected) {
								if((pPixel->rgbReserved != 0x00) || selectionBoundingRect.PtInRect(pt2)) {
									pPixel->rgbReserved = 0xFF;
								}
							} else if(labelBoundingRect.PtInRect(pt2)) {
								if(pPixel->rgbReserved == 0x00) {
									pPixel->rgbReserved = 0xFF;
								}
							}
						} else {
							// items are not themed
							if(itemIsSelected) {
								if((pPixel->rgbReserved == 0x00) || selectionBoundingRect.PtInRect(pt2)) {
									if(subItemImages) {
										BOOL changeAlpha = TRUE;
										for(int i = 0; i < columnCount - 1; ++i) {
											if(::PtInRect(&pSubItemImageRectangles[i], pt2)) {
												changeAlpha = !pSubItemImageHasAlpha[i];
												break;
											}
										}
										if(changeAlpha) {
											pPixel->rgbReserved = 0xFF;
										}
									} else {
										pPixel->rgbReserved = 0xFF;
									}
								}
							} else {
								if(pPixel->rgbReserved == 0x00) {
									pPixel->rgbReserved = 0xFF;
								}
							}
						}
					}
				} else {
					// layout is left to right
					if(maskMemoryDC.GetPixel(pt.x, pt.y) == 0x00000000) {
						if(themedListItems) {
							if(itemIsSelected) {
								if((pPixel->rgbReserved != 0x00) || selectionBoundingRect.PtInRect(pt)) {
									pPixel->rgbReserved = 0xFF;
								}
							} else if(labelBoundingRect.PtInRect(pt)) {
								if(pPixel->rgbReserved == 0x00) {
									pPixel->rgbReserved = 0xFF;
								}
							}
						} else {
							// items are not themed
							if(itemIsSelected) {
								if((pPixel->rgbReserved == 0x00) || selectionBoundingRect.PtInRect(pt)) {
									if(subItemImages) {
										BOOL changeAlpha = TRUE;
										for(int i = 0; i < columnCount - 1; ++i) {
											if(::PtInRect(&pSubItemImageRectangles[i], pt)) {
												changeAlpha = !pSubItemImageHasAlpha[i];
												break;
											}
										}
										if(changeAlpha) {
											pPixel->rgbReserved = 0xFF;
										}
									} else {
										pPixel->rgbReserved = 0xFF;
									}
								}
							} else {
								if(pPixel->rgbReserved == 0x00) {
									pPixel->rgbReserved = 0xFF;
								}
							}
						}
					}
				}
			}
		}
	}

	memoryDC.SelectBitmap(hPreviousBitmap);
	maskMemoryDC.SelectBitmap(hPreviousBitmapMask);
	if(hPreviousFont) {
		memoryDC.SelectFont(hPreviousFont);
	}

	// create the imagelist
	HIMAGELIST hDragImageList = ::ImageList_Create(dragImageSize.cx, dragImageSize.cy, (IsComctl32Version600OrNewer() ? ILC_COLOR32 : ILC_COLOR24) | ILC_MASK, 1, 0);
	::ImageList_SetBkColor(hDragImageList, CLR_NONE);
	::ImageList_Add(hDragImageList, dragImage, dragImageMask);

	SECUREFREE(pItemTextBuffer);
	::VariantClear(&vTileViewColumns);
	ReleaseDC(hCompatibleDC);
	if(pSubItemImageRectangles) {
		delete[] pSubItemImageRectangles;
	}
	if(pSubItemImageHasAlpha) {
		delete[] pSubItemImageHasAlpha;
	}

	return hDragImageList;
}
That's it - at least for classic drag images. Aero drag images are a different beast.
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
cordov
Cadet
Posts: 7
Joined: 14 Jan 2008, 10:54

Post by cordov »

Classic drag image is a huge beast for me, I don't want to imagine what kind of animal could be the Aero one!!

Maybe, we'll change all the implementetion of the code.
I think your sample could really help me to reach my target.

Thank you very much
Luigi
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Post by TiKu »

cordov wrote:Classic drag image is a huge beast for me, I don't want to imagine what kind of animal could be the Aero one!!
Actually my code for Aero drag images is much shorter, because Aero drag images have the advantage of being the same for all listview view modes.

/edit: Okay, not quite right. The code for the item count label and the drop descriptions increases the size of Aero drag image code so that it's about the same size in the end.
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
User avatar
TiKu
Administrator
Administrator
Posts: 832
Joined: 28 Sep 2004, 21:10
Location: München
Contact:

Post by TiKu »

Let me know if you want to know how to get Vista drag images. I got the OK to post the details here.
Crunching for Fab36_Folding-Division at Folding@Home. Join Fab36/Fab30! - Folding@Home and BOINC
Boycott DRM! Boycott HDCP!
gregorj
Cadet
Posts: 1
Joined: 29 Aug 2008, 22:04

Post by gregorj »

Hey TiKu, could you post details on how to get Vista drag images. Ive been trying to get it to work for a past month or so

thx a lot :)
Post Reply