Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Implementing Reusable Drag & Drop Classes

, 4 Jun 2001 CPOL
This article provides a set of reusable drag and drop classes
/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.
   Author: Leon Finker  1/2001
**************************************************************************/
//controls.h
#ifndef __CONTROLS_H__
#define __CONTROLS_H__

#include "editdroptarget.h"
#include "listviewdroptarget.h"
#include "staticdroptarget.h"
#include "treedroptarget.h"

class CEditBox : public CWindowImpl<CEditBox, WTL::CEdit>, public WTL::CEditCommands<CEditBox>
{
	CIDropTarget* m_pDropTarget;
public:
	CEditBox():m_pDropTarget(NULL){}

	BEGIN_MSG_MAP(CEditBox)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		CHAIN_MSG_MAP_ALT(CEditCommands<CEditBox>, 1)
		DEFAULT_REFLECTION_HANDLER()
	END_MSG_MAP()

	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RevokeDragDrop(m_hWnd); //calls release
		m_pDropTarget=NULL;
		return 0;
	}

	bool InitDragDrop()
	{
			m_pDropTarget = new CEditDropTarget(m_hWnd);
			if(m_pDropTarget == NULL)
				return false;
			m_pDropTarget->AddRef();

			if(FAILED(RegisterDragDrop(m_hWnd,m_pDropTarget))) //calls addref
			{
				m_pDropTarget = NULL;
				return false;
			}
			else
				m_pDropTarget->Release(); //i decided to AddRef explicitly after new

			FORMATETC ftetc={0};
			ftetc.cfFormat = CF_TEXT;
			ftetc.dwAspect = DVASPECT_CONTENT;
			ftetc.lindex = -1;
			ftetc.tymed = TYMED_ISTREAM;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.tymed = TYMED_HGLOBAL;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.cfFormat=CF_HDROP;
			m_pDropTarget->AddSuportedFormat(ftetc);
		return true;
	}


};

class CTreeBox : public CWindowImpl<CTreeBox, WTL::CTreeViewCtrl>
{
	CIDropTarget* m_pDropTarget;
public:
	BEGIN_MSG_MAP(CTreeBox)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		REFLECTED_NOTIFY_HANDLER(IDC_TREE1, TVN_BEGINDRAG, OnBegindrag)
		DEFAULT_REFLECTION_HANDLER()
	END_MSG_MAP()
	
	CTreeBox():m_pDropTarget(NULL){}
	
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RevokeDragDrop(m_hWnd); //calls release
		m_pDropTarget=NULL;
		return 0;
	}

	LRESULT OnBegindrag(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		NMTREEVIEW* pnmtv = (NMTREEVIEW*)pnmh;
		USES_CONVERSION;
		CComBSTR bstr;
		GetItemText(pnmtv->itemNew.hItem, bstr.m_str);
		
		CIDropSource* pdsrc = new CIDropSource;
		if(pdsrc == NULL)
			return 0;
		pdsrc->AddRef();

		CIDataObject* pdobj = new CIDataObject(pdsrc);
		if(pdobj == NULL)
			return 0;
		pdobj->AddRef();

		FORMATETC fmtetc = {0};
		fmtetc.cfFormat = CF_TEXT;
		fmtetc.dwAspect = DVASPECT_CONTENT;
		fmtetc.lindex = -1;
		fmtetc.tymed = TYMED_HGLOBAL;

		STGMEDIUM medium = {0};
		medium.tymed = TYMED_HGLOBAL;
		TCHAR* str = OLE2T(bstr.m_str);

		medium.hGlobal = GlobalAlloc(GMEM_MOVEABLE, strlen(str)+1); //for NULL
		TCHAR* pMem = (TCHAR*)GlobalLock(medium.hGlobal);
		strcpy(pMem,str);
		GlobalUnlock(medium.hGlobal);

		pdobj->SetData(&fmtetc,&medium,TRUE);
		
		CDragSourceHelper dragSrcHelper;
		// get drag image from the window through DI_GETDRAGIMAGE (treeview seems to already support it)
		dragSrcHelper.InitializeFromWindow(m_hWnd, pnmtv->ptDrag, pdobj);
		DWORD dwEffect;
		HRESULT hr = ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_COPY, &dwEffect);
		pdsrc->Release();
		pdobj->Release();
		return 0;
	}

	bool InitDragDrop()
	{
			m_pDropTarget = new CTreeDropTarget(m_hWnd);
			if(m_pDropTarget == NULL)
				return false;
			m_pDropTarget->AddRef();

			if(FAILED(RegisterDragDrop(m_hWnd,m_pDropTarget))) //calls addref
			{
				m_pDropTarget = NULL;
				return false;
			}
			else
				m_pDropTarget->Release(); //i decided to AddRef explicitly after new

			FORMATETC ftetc={0};
			ftetc.cfFormat = CF_TEXT;
			ftetc.dwAspect = DVASPECT_CONTENT;
			ftetc.lindex = -1;
			ftetc.tymed = TYMED_HGLOBAL;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.cfFormat=CF_HDROP;
			m_pDropTarget->AddSuportedFormat(ftetc);
		return true;
	}
};

class CStaticBox : public CWindowImpl<CStatic,WTL::CStatic>
{
	CIDropTarget* m_pDropTarget;
	bool m_bDragMode;
public:
	BEGIN_MSG_MAP(CStaticBox)
		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
		MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		DEFAULT_REFLECTION_HANDLER()
	END_MSG_MAP()

	CStaticBox():m_bDragMode(false), m_pDropTarget(NULL){}
	
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RevokeDragDrop(m_hWnd); //calls release
		m_pDropTarget=NULL;
		return 0;
	}

	LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CMenu menu;
		if(!menu.CreatePopupMenu())
			return 0;
		menu.AppendMenu(MF_STRING, 1, "Copy");
		int nCmd = menu.TrackPopupMenu(TPM_RETURNCMD,LOWORD(lParam),HIWORD(lParam),m_hWnd);
		if(nCmd == 1)
		{
			CIDataObject* pdobj = new CIDataObject(NULL);
			if(pdobj == NULL)
				return 0;
			pdobj->AddRef();
			BITMAP bmap;
			HBITMAP hBitmap = (HBITMAP)OleDuplicateData(GetBitmap(), CF_BITMAP, NULL);
			GetObject(hBitmap, sizeof(BITMAP), &bmap);
			RECT rc={0,0,bmap.bmWidth,bmap.bmHeight};
			HDC hdc = GetDC();
			FORMATETC fmtetc = {0};
			STGMEDIUM medium = {0};
			fmtetc.dwAspect = DVASPECT_CONTENT;
			fmtetc.lindex = -1;
			fmtetc.cfFormat = CF_ENHMETAFILE;
			fmtetc.tymed = TYMED_ENHMF;
			HDC hMetaDC = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
			HDC hdcMem = CreateCompatibleDC(hdc);
			HGDIOBJ hOldBmp = ::SelectObject(hdcMem, hBitmap);
			::BitBlt(hMetaDC, 0, 0, rc.right, rc.bottom, hdcMem, 0, 0, SRCCOPY);
			::SelectObject(hdcMem, hOldBmp);
			medium.hEnhMetaFile = CloseEnhMetaFile(hMetaDC);
			DeleteDC(hdcMem);
			ReleaseDC(hdc);
			medium.tymed = TYMED_ENHMF;
			pdobj->SetData(&fmtetc,&medium,TRUE);
			OleSetClipboard(pdobj);
			OleFlushClipboard(); //render the data
			pdobj->Release();
		}
		return 0;
	}

	LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		m_bDragMode = true;
		return 0;
	}

	LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		if(m_bDragMode && wParam == MK_LBUTTON)
		{
			CIDropSource* pdsrc = new CIDropSource;
			if(pdsrc == NULL)
				return 0;
			pdsrc->AddRef();

			CIDataObject* pdobj = new CIDataObject(pdsrc);
			if(pdobj == NULL)
				return 0;
			pdobj->AddRef();

			FORMATETC fmtetc = {0};
			STGMEDIUM medium = {0};
			fmtetc.dwAspect = DVASPECT_CONTENT;
			fmtetc.lindex = -1;
			//////////////////////////////////////
			fmtetc.cfFormat = CF_BITMAP;
			fmtetc.tymed = TYMED_GDI;			
			medium.tymed = TYMED_GDI;
			HBITMAP hBitmap = (HBITMAP)OleDuplicateData(GetBitmap(), fmtetc.cfFormat, NULL);
			medium.hBitmap = hBitmap;
			pdobj->SetData(&fmtetc,&medium,FALSE);
			//////////////////////////////////////
			BITMAP bmap;
			GetObject(hBitmap, sizeof(BITMAP), &bmap);
			RECT rc={0,0,bmap.bmWidth,bmap.bmHeight};
			HDC hdc = GetDC();
			fmtetc.cfFormat = CF_ENHMETAFILE;
			fmtetc.tymed = TYMED_ENHMF;
			HDC hMetaDC = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
			HDC hdcMem = CreateCompatibleDC(hdc);
			HGDIOBJ hOldBmp = ::SelectObject(hdcMem, hBitmap);
			::BitBlt(hMetaDC, 0, 0, rc.right, rc.bottom, hdcMem, 0, 0, SRCCOPY);
			::SelectObject(hdcMem, hOldBmp);
			medium.hEnhMetaFile = CloseEnhMetaFile(hMetaDC);
			DeleteDC(hdcMem);
			ReleaseDC(hdc);
			medium.tymed = TYMED_ENHMF;
			pdobj->SetData(&fmtetc,&medium,TRUE);
			//////////////////////////////////////
			CDragSourceHelper dragSrcHelper;
			POINT pt={LOWORD(lParam), HIWORD(lParam)};
			//rc is off because i used static box with style where bmp doesn't take whole window
			dragSrcHelper.InitializeFromBitmap(hBitmap, pt, rc, pdobj); //will own the bmp
			DWORD dwEffect;
			HRESULT hr = ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_COPY|DROPEFFECT_MOVE, &dwEffect);
			pdsrc->Release();
			pdobj->Release();
			m_bDragMode = false;
		}
		return 0;
	}
	
	bool InitDragDrop()
	{
			m_pDropTarget = new CStaticDropTarget(m_hWnd);
			if(m_pDropTarget == NULL)
				return false;
			m_pDropTarget->AddRef();

			if(FAILED(RegisterDragDrop(m_hWnd,m_pDropTarget))) //calls addref
			{
				m_pDropTarget = NULL;
				return false;
			}
			else
				m_pDropTarget->Release(); //i decided to AddRef explicitly after new

			FORMATETC ftetc={0};
			ftetc.cfFormat = CF_BITMAP;
			ftetc.dwAspect = DVASPECT_CONTENT;
			ftetc.lindex = -1;
			ftetc.tymed = TYMED_GDI;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.cfFormat = CF_DIB;
			ftetc.tymed = TYMED_HGLOBAL;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.cfFormat = CF_HDROP;
			ftetc.tymed = TYMED_HGLOBAL;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.cfFormat = CF_ENHMETAFILE;
			ftetc.tymed = TYMED_ENHMF;
			m_pDropTarget->AddSuportedFormat(ftetc);
		return true;
	}
};

class CListViewBox : public CWindowImpl<CListViewBox,WTL::CListViewCtrl>
{
	CIDropTarget* m_pDropTarget;
public:	
	BEGIN_MSG_MAP(CTreeBox)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		REFLECTED_NOTIFY_HANDLER(IDC_LISTVIEW, LVN_BEGINDRAG, OnBegindragListview)
		DEFAULT_REFLECTION_HANDLER()
	END_MSG_MAP()

	CListViewBox():m_pDropTarget(NULL){}

	bool InitDragDrop()
	{
			m_pDropTarget = new CListViewDropTarget(m_hWnd);
			if(m_pDropTarget == NULL)
				return false;
			m_pDropTarget->AddRef();

			if(FAILED(RegisterDragDrop(m_hWnd,m_pDropTarget))) //calls addref
			{
				m_pDropTarget = NULL;
				return false;
			}
			else
				m_pDropTarget->Release(); //i decided to AddRef explicitly after new

			FORMATETC ftetc={0};
			ftetc.cfFormat = CF_TEXT;
			ftetc.dwAspect = DVASPECT_CONTENT;
			ftetc.lindex = -1;
			ftetc.tymed = TYMED_HGLOBAL;
			m_pDropTarget->AddSuportedFormat(ftetc);
			ftetc.cfFormat=CF_HDROP;
			m_pDropTarget->AddSuportedFormat(ftetc);
		return true;
	}

	LRESULT OnBegindragListview(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		USES_CONVERSION;
		NMLISTVIEW* pnmlv = (NMLISTVIEW*)pnmh;
		CComBSTR bstr;
		GetItemText(pnmlv->iItem, 0, bstr.m_str);
		
		CIDropSource* pdsrc = new CIDropSource;
		if(pdsrc == NULL)
			return 0;
		pdsrc->AddRef();

		CIDataObject* pdobj = new CIDataObject(pdsrc);
		if(pdobj == NULL)
			return 0;
		pdobj->AddRef();

		FORMATETC fmtetc = {0};
		fmtetc.cfFormat = CF_TEXT;
		fmtetc.dwAspect = DVASPECT_CONTENT;
		fmtetc.lindex = -1;
		fmtetc.tymed = TYMED_HGLOBAL;

		STGMEDIUM medium = {0};
		medium.tymed = TYMED_HGLOBAL;
		TCHAR* str = OLE2T(bstr.m_str);
		
		medium.hGlobal = GlobalAlloc(GMEM_MOVEABLE, strlen(str)+1); //for NULL
		TCHAR* pMem = (TCHAR*)GlobalLock(medium.hGlobal);
		strcpy(pMem,str);
		GlobalUnlock(medium.hGlobal);
		pdobj->SetData(&fmtetc,&medium,TRUE);
		/////////////////////////////////////////
		HRESULT hr;
		IStream* pStream;
		/*IStream* pStream;
		//ie5.0 API
		hr = SHCreateStreamOn("readme.txt",STGM_READ,&pStream);
		medium.pstm = pStream;
		fmtetc.tymed = TYMED_ISTREAM;
		medium.tymed = TYMED_ISTREAM;
		pdobj->SetData(&fmtetc,&medium,TRUE);*/
		/////////////////////////////////////////
		CreateStreamOnHGlobal(NULL, TRUE, &pStream);
		DWORD dwWritten;
		pStream->Write(str, strlen(str)+1, &dwWritten);
		LARGE_INTEGER dlibMove; dlibMove.QuadPart = 0;
		pStream->Seek(dlibMove,STREAM_SEEK_SET,NULL);
		medium.pstm = pStream;
		fmtetc.tymed = TYMED_ISTREAM;
		medium.tymed = TYMED_ISTREAM;
		pdobj->SetData(&fmtetc,&medium,TRUE);
		/////////////////////////////////////////
		
		CDragSourceHelper dragSrcHelper;
		if(1)	// get drag image from the window through DI_GETDRAGIMAGE (listview seems to already support it)
			dragSrcHelper.InitializeFromWindow(m_hWnd, pnmlv->ptAction, pdobj);
		else   // load drag image from bitmap
		{
		RECT rc;
		ListView_GetItemRect(m_hWnd, pnmlv->iItem, &rc, LVIR_SELECTBOUNDS);
		HBITMAP hBitmap = LoadBitmap(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_DRAGIMG));
		dragSrcHelper.InitializeFromBitmap(hBitmap, pnmlv->ptAction, rc, pdobj);
		}
		DWORD dwEffect;
		hr = ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_COPY, &dwEffect);
		pdsrc->Release();
		pdobj->Release();
		//ReleaseStgMedium(&medium); // SetData fRelease flag is set, so DataObject owns the data don't free the data
		return 0;
	}
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RevokeDragDrop(m_hWnd); //calls release
		m_pDropTarget=NULL;
		return 0;
	}
};

#endif //__CONTROLS_H__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Leon Finker

United States United States
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150414.1 | Last Updated 5 Jun 2001
Article Copyright 2001 by Leon Finker
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid