The Shell Drag/Drop Helper Object Part 1: IDropTargetHelper

 

Raymond Chen
Microsoft Corporation

February 2000

Summary: The Microsoft® Windows® 2000 shell provides a new object, the shell drag/drop helper object, which allows you to take advantage of the shell drag/drop user interface, including features such as drag images and alpha blending. This is the first in a pair of articles that describe how you can use shell drag/drop helper object in your application to provide a richer user experience. (5 printed pages)

Contents

Accessing the IDropTargetHelper Interface Using the IDropTargetHelper Interface Painting in the Drag Loop Halftime

ms997500.ddhelp_pt1_1(en-us,MSDN.10).gif

Figure 1. Drawing drag/drop feedback
with the shell drag/drop helper object

Accessing the IDropTargetHelper Interface

The IDropTargetHelper interface allows you to provide more meaningful feedback when the user drags a shell object (or any other object that uses the shell drag/drop helper object) over your drop target. The interface is extremely simple to use; the only method that is the slightest bit tricky is IDropTargetHelper::Show, but I'll help you through it.

Start by creating a shell drag/drop helper object with the CoCreateInstance function, specifying IID_IdropTargetHelper as the interface. Note that each drop target must create its own helper object (typically as a member variable).

Because I have limited space in this article, I will use ATL smart pointers to finesse the issue of reference counting and NULL pointer checking:

class CDropTarget : public IDropTarget {
...
    CDropTarget(HWND hwnd);
    ~CDropTarget();
private:
    CComPtr<IDropTargetHelper> m_spdth;
    CComPtr<IDataObject> m_spdtoDragging;   /* The object being dragged */
    HWND m_hwnd;                            /* The handle of our window */
};

CDropTarget::CDropTarget(HWND hwnd)         /* Constructor */
{
    /* This call might fail, in which case OLE sets m_pdth = NULL */
    CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
                     IID_IDropTargetHelper, (LPVOID*)&m_pdth);

    m_hwnd = hwnd;
    RegisterDragDrop(m_hwnd, this);

    /* ... other constructor things go here ... */
}

CDropTarget::~CDropTarget()                 /* Destructor */
{
    // ATL smart pointers automatically release on destruction
    RevokeDragDrop(m_hwnd);
}

Because earlier versions of Microsoft Windows do not implement a shell drag/drop helper object, the CoCreateInstance function might fail, so make sure to check that the m_pdth member is not NULL before using it. (Clever programmers can come up with an alternate solution involving dummy objects.)

Using the IDropTargetHelper Interface

As I've already mentioned, the shell drag/drop helper object is extremely simple to use. Merely call the appropriate IDropTargetHelper method from your corresponding IDropTarget method. The drag/drop helper object will then do all the work of drawing/erasing the object being dragged.

For example, here is code from my sample drop target. (Figure 1 is a screenshot of the program in action.)

HRESULT CDropTarget::DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    if (m_spdth) {              /* Use the helper if we have one */
        POINT pt = { ptl.x, ptl.y };
        m_spdth->DragEnter(m_hwnd, pdto, &pt, *pdwEffect);
    }
    m_spdtoDragging = pdto;     /* Remember who is being dragged */

    /* Do your standard drop target work here... */

    return S_OK;
}

HRESULT CDropTarget::DragLeave()
{
    if (m_spdth) {              /* Use the helper if we have one */
        m_spdth->DragLeave();
    }
    m_spdtoDragging->Release(); /* Nobody is being dragged any more */

    /* Do your standard drop target work here... */

    return S_OK;
}

HRESULT CDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    if (m_spdth) {              /* Use the helper if we have one */
        POINT pt = { ptl.x, ptl.y };
        m_spdth->DragOver(&pt, *pdwEffect);
    }
    /* Do your standard drop target work here... */

    return S_OK;
}

HRESULT CDropTarget::Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
    if (m_spdth) {              /* Use the helper if we have one */
        POINT pt = { ptl.x, ptl.y };
        m_spdth->Drop(pdto, &pt, *pdwEffect);
    }
    m_spdtoDragging->Release(); /* Nobody is being dragged any more */

    // Do your standard drop target work here... Our sample merely says
    // "nothing happened".
    *pdwEffect = DROPEFFECT_NONE;

    return S_OK;
}

You'll see that in addition to calling the drag/drop helper object at the start of each method, we also keep track of the data object being dragged. This extra bit of bookkeeping solves a subtle problem, which we will see soon.

Painting in the Drag Loop

As things stand now, when the user drags an object over our window, attempts to paint the drop target window will sometimes fail. This is a problem if you want your drop target window to animate during the drag loop. For example, when you drag an object over the edge of an Explorer window, the window scrolls in the direction of the edge, and when you drag an object over an item within the Explorer window, the item is drawn in a special color to indicate it is the target of the drop.

To solve this problem, we use the remaining method of the IDropTargetHelper interface:

// WM_PAINT handler...

    if (m_spdtoDragging && m_spdth) {
        m_spdth->Show(FALSE);
    }

    // put regular painting code here

    if (m_spdtoDragging && m_spdth) {
        m_spdth->Show(TRUE);
    }

The IDropTargetHelper::Show method lets you explicitly hide and show the drag image. In some system configurations, attempts to paint while the drag image is displayed will be ignored, so when you want to paint, you have to hide the drag image, do your painting, and then show it again.

Halftime

As you can see, using the IDropTargetHelper interface is quite simple indeed. With just a few lines of code, your application can take advantage of Windows 2000 graphics features without even having to know how they work. What's more, as the shell continues to develop, the shell drag/drop helper object will continue to provide you with access to the shell drag/drop UI. The second article in this series will discuss the other half of drag/drop: The drag source.