Introduction
I tried to create as generic a Drag and Drop classes as possible. Here is
what I came up with. I think it's a good starting point if you're looking to add
drag and drop support to your app.
The demo project has sample code for various clipboard formats:
CF_TEXT
CF_HDROP
CF_BITMAP
CF_DIB
CF_ENHMETAFILE
and MEDIUMs
:
TYMED_HGLOBAL
TYMED_ISTREAM
TYMED_ENHMF
TYMED_GDI
.
Some screenshots of image drag and drop from static window:
(Fig. 1. Dragging from static window to WordPad)
(Fig. 2. Using Clipboard)
(Fig. 3. Pasting the above clipboard contents into WordPad)
Usage:
To enable your window as a
DropTarget:
- Derive a class from
CIDropTarget
.
- Override
OnDrop
. Return true or false from this method. If true,
base class will free the medium. If false, it won't free the medium.
- Call
::RegisterDragDrop
for your window.
- Add Supported formats by calling
CIDropTarget::AddSuportedFormat
.
- Optionally override other methods such as
DragOver
and DragLeave
.
I
used it for the tree to highlight the current item.
Example:
class CTreeDropTarget : public CIDropTarget
{
public:
virtual bool OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium, DWORD *pdwEffect)
{
if(pFmtEtc->cfFormat == CF_TEXT && medium.tymed == TYMED_HGLOBAL)
{
}
return true;
}
};
In your Window derived class create a member of CTreeDropTarget
. Then initialize it like this:
{
m_pDropTarget = new CTreeDropTarget(m_hWnd);
RegisterDragDrop(m_hWnd,m_pDropTarget);
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);
}
That's all for drop target.
To enable your window as
the Drag and Drop source:
- Catch the Windows message that initiates the drag and drop such as
TVN_BEGINDRAG
.
- In the message function handler create new
CIDataObject
and CIDropSource
.
- Create the clipboard formats and medium for those formats.
- Call
SetData
to add the clipboard formats and medium to
DataObject. Second parameter to SetData
indicates if
DataObject should take the ownership of medium or not.
If set to TRUE, then DataObject takes the ownership of your
medium, you don't need to free it. Otherwise it will make a copy of your
medium without releasing the one you gave it.
Eaxmple:
LRESULT OnBegindrag(...)
{
CIDropSource* pdsrc = new CIDropSource;
CIDataObject* pdobj = new CIDataObject(pdsrc);
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;
pdobj->SetData(&fmtetc,&medium,TRUE);
::DoDragDrop(pdobj, pdsrc, DROPEFFECT_COPY, &dwEffect);
}
To use the shell's drag image manager (comes
with Windows 2000):
You don't need to add the support for it if you are
acting as drop target. It is encapsulated in
CIDropTarget class.
If you're acting as data source:
- Create an instance of CDragSourceHelper before calling ::DoDragDrop.
- Call CDragSourceHelper::InitializeFromWindow or
CDragSourceHelper::InitializeFromBitmap.
Adding the Copy/Paste through clipboard is not much work
either.
Example:
LRESULT OnContextMenu(...)
{
CIDataObject* pdobj = new CIDataObject(NULL);
pdobj->SetData(&fmtetc,&medium,TRUE);
OleSetClipboard(pdobj);
OleFlushClipboard();
}
References: