Implementing a DropTarget in ATL

Environment: ATL

Implementing a DropTarget in ATL

Making a control a drop-target in ATL is not something you do very easily. The only help I found in MSDN was an example that didn't work (surprise) until I changed the datatypes of two parameters, as though they did it on purpose. That frustration out of the way, I decided to write a class that will make implementing a drop-target easy as pie. Here it is.

This class takes care of the drag-target functionality in ATL for any CWindow derivable control(= anything with an hWnd). Functions/macros you need to use are the following:

USING_CFDRAGDROP—This is a macro that needs to be included globally.

Because CFDragDropWindow has some static functions and members that need to be defined ONCE and ONCE only, include this macro in such a way that the compiler will only see it once, in the container-class that uses this class define a static function of the DRAGDROPNOTIF prototype, such as:

static HRESULT ReadFiles(FileVector vecStrings,
                         HWND hwnd,
                         void * P){ ... }

in which the first argument will be a vector with the names of the dropped files and the second argument the HWND of the control that the drop occurred on and the third argument the this pointer of your container-class. Next, call CFDragDropWindow's SetCallBack with this function as its first parameter and the 'this' pointer of your container-class as its second argument.

Then, call Attach with the hWnd belonging to the control you want to superclass. Now, when one or more files are dropped on the control, the DRAGDROPNOTIF function in your container-class will get called with as its arguments the dropped filenames and the 'this' pointer of your class (which you can easily cast into your class' datatype so you can use non-static methods/properties).

Example: Using CFDragDrop

class CMainDlg: public CDialogImpl<CMainDlg>
    // the common ATL Dialog, for example
{
  ...

  CFDragDropWindow m_CFDragDropWindow;
  LRESULT OnInitDialog(UINT , WPARAM , LPARAM , BOOL& )
  {
    ...

  HWND hWndTemp = GetDlgItem(IDC_EDIT_SOURCE);
  m_CFDragDropWindow.SetCallBack(ReadFiles,this);
  m_CFDragDropWindow.Attach(hWndTemp);
  return 0;
  }
  static HRESULT ReadFiles(FileVector vecStrings,HWND hwnd,
                           void * P)
  {
    USES_CONVERSION;
    CMainDlg * pThis = (CMainDlg*)P;    // use this to call
                                        // non-static methods
    CComBSTR strTemp = vecStrings[0];
    ::SetWindowText(hwnd,W2A(strTemp));
    return S_OK;
  }
};

The CFDragDropWindow Class

#ifndef CF_DRAGDROP_WINDOW_
#define CF_DRAGDROP_WINDOW_
#include <shellapi.h>
#pragma comment(lib,"shell32.lib")
#include <vector>
typedef std::vector<CComBSTR> FileVector;
#include <map>

/*
December 2002
Copyright © 2002 Frans Nagel */

struct ProcData
{
  void * pContainer;
  WNDPROC OldProc;
};
typedef std::map<HWND,ProcData,std::less<HWND> > ProcMap;

typedef HRESULT (*DRAGDROPNOTIF)(FileVector,
                                 HWND hwndDropTarget,
                                 void * pContainer);
class CFDragDropWindow  : public CWindow
{
public:

  CFDragDropWindow(){m_DragDropNotif = 0;}
  static BOOL CALLBACK EditDrop(HWND, unsigned, WPARAM, LPARAM);
  void Attach(HWND hwnd)
  {
    ATLASSERT(m_DragDropNotif != 0);    // call SetCallBack first
    CWindow::Attach(hwnd);
    ::DragAcceptFiles(m_hWnd,TRUE);
         m_pDragDropProc = (WNDPROC)MakeProcInstance
                           (EditDrop, hInst);
         if (m_pDragDropProc)
         m_pOldProc = (WNDPROC)::SetWindowLong(m_hWnd,
                       GWL_WNDPROC,
                      (DWORD)(WNDPROC)m_pDragDropProc);
    ProcData pd;
    pd.pContainer = m_pContainer ;
    pd.OldProc = m_pOldProc;
    m_ProcMap.insert(ProcMap::value_type(hwnd,pd));
  }
  void SetCallBack(DRAGDROPNOTIF ntf,void * pContainer)
  {
    //the function loaded up here needs to be
    //implemented by the container class
    m_DragDropNotif = ntf;
    m_pContainer = pContainer;
  }
  ~CFDragDropWindow()
  {
    if (m_pDragDropProc)
        FreeProcInstance(m_pDragDropProc);
  }
protected:
  static void * m_pContainer;
  static DRAGDROPNOTIF m_DragDropNotif;
  static WNDPROC m_pDragDropProc, m_pOldProc;
  static FileVector m_vecStrings;
  static ProcMap m_ProcMap;
};
#define USING_CFDRAGDROP \
DRAGDROPNOTIF CFDragDropWindow::m_DragDropNotif;\
WNDPROC CFDragDropWindow::m_pDragDropProc;\
WNDPROC CFDragDropWindow::m_pOldProc;\
ProcMap CFDragDropWindow::m_ProcMap;\
void * CFDragDropWindow::m_pContainer;\
FileVector CFDragDropWindow::m_vecStrings;\
BOOL CALLBACK CFDragDropWindow::EditDrop(HWND,
                                         unsigned,
                                         WPARAM,
                                         LPARAM);\
BOOL CALLBACK CFDragDropWindow::EditDrop(HWND hWnd,
                                  unsigned message,
                                  WPARAM wParam,
                                  LPARAM lParam)\
   {\
     int wFilesDropped,i = 0;\
  char szTemp[MAX_PATH];\
  ProcMap::iterator iter;\
    WNDPROC wndProc;\
    switch (message)\
      {\
      case WM_DROPFILES:\
    m_vecStrings.clear();\
     while(true)\
    {\
      wFilesDropped = DragQueryFile((HDROP)wParam, i++,szTemp,
                      255);\
      if(!wFilesDropped)\
        break;\
      m_vecStrings.push_back(szTemp);\
    }\
        DragFinish((HDROP)wParam);\
        iter = m_ProcMap.find(hWnd);\
        m_DragDropNotif(m_vecStrings,hWnd,iter->
                        second.pContainer);\
        break;\
    default:\
    iter = m_ProcMap.find(hWnd);\
      wndProc = iter->second.OldProc;\
        return CallWindowProc((WNDPROC)wndProc, hWnd, message,\
              wParam, lParam);\
     }\
     return TRUE;\
   } 

#endif

Downloads

Download demo project - 35 Kb


Comments

  • VS.Net 2003 doesn't like stl:map

    Posted by kenkyee on 05/03/2005 01:42pm

    Won't load your ATL object and blows up during regsvr32. Reimplement ProcMap using std::vector and iterate through it looking for the hwnd instead (add an hwnd variable to ProcData and tuck it there).

    Reply
  • some mods to get it to compile in VS.Net

    Posted by kenkyee on 05/02/2005 06:30pm

    In FDragDropWindow.h, I had to comment out this line: /*BOOL CALLBACK CFDragDropWindow::EditDrop(HWND, unsigned, WPARAM, LPARAM);*/\ Also had to modify char szTemp[MAX_PATH];\ to WCHAR szTemp[MAX_PATH];\ But now, I get a cryptic regsvr32 error when I build my project after adding a CFDragDropWindow member variable: error PRJ0019: A tool returned an error code from "Performing registration"

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds