Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dropping Files into a WTL Window (The Easy Way)

0.00/5 (No votes)
20 Apr 2004 1  
A mix-in class to help drop files into WTL windows.

Sample Image (Dialog version)

Introduction

Every day, many of us drop files on windows in order to get them to 'do' something: an editor (text or graphics), a word processor, an electronic worksheet, a media player, and even Visual Studio usually opens the file: a shortcut is created when dropping the file on top of the 'Start' button; some utilities add a shortcut to a toolbar, when dropping the file on that toolbar.

The API way to do it, is to add WS_EX_ACCEPTFILES to the extended style of your dialog, view or child window, and handle WM_DROPFILES.

Since most of the WM_DROPFILES handlers are quite similar, the WTL way to do it is to make a mixin class which handles the boring parts of the job, inherit from it, and add just your own logic.

After that, you can add WM_DROPFILES handling to your windows with about a dozen lines of code.

Background

The WM_DROPFILES handler usually does the following actions:

  • Tests to see that the drop happened inside the client area of the relevant window.
  • Tests to see that the application is ready to handle dropped files.
  • Checks how many files were dropped.
  • In a loop (since multiple files can be dropped at once): A. Gets the name (a fully qualified path is given) of each dropped file. B. Does some application-specific handling.
  • Releases memory and resources used to manage the drag and drop operation.
  • Does application-specific resource cleanup, if needed.

As you see, most of the functionality is boilerplate: only the actions marked in bold are application-specific. Those are the only ones you'll have to write, if you decide to use CDropFilesHandler.

Using the code

There are basically two kinds of windows which will handle dropped files: dialogs and frame windows.

Using CDropFilesHandler<CMyDialog> (with dialogs):

  • In the resource editor, mark the checkbox "Accept Files" in the tab "Extended Styles" (see image below), otherwise the WM_DROPFILES message will never arrive.

The sample dialog's properties

  • #include <DropFilesHandler.h> (which should be in your WTL\Include\CustomExtensions folder).
  • Inherit your class (from now on, CMyDialog) from CDropFilesHandler<CMyDialog>.
  • Forward messages, by adding CHAIN_MSG_MAP(CDropFilesHandler<CMyDialog>) to your message map.
  • Implement three functions:
    • BOOL IsReadyForDrop(void) - Called once for each drop operation, before entering the dropped files loop. Return TRUE if your dialog is ready to accept dropped files, FALSE otherwise.
    • BOOL HandleDroppedFile(LPCTSTR szBuff) - This is the function called inside the loop, once per dropped file, and it should return FALSE to break the loop. This one might implement the real 'handling' of the drop (e.g., by pasting the file's name in an edit box, or, in a SDI/MDI/Multi SDI application, by opening the file).
    • void EndDropFiles(void) - This function is called once, after exiting the loop. You can leave this as an empty inline, but if your file handling is not trivial, and you want to handle several files at once, putting all their names in a container during the loop (std::list<WTL::CString> comes to mind) and starting a worker thread here to really handle them all can be an option.

This is the dialog class in the dialog sample project (relevant parts in bold):

// maindlg.h : interface of the CMainDlg class
//
/////////////////////////////////////////////////////////////////////

#include "DropFileHandler.h" // Include the relevant file...


class CMainDlg : public CDialogImpl<CMainDlg>,
              // Add CDropFilesHandler to your inheritance list...
                 public CDropFilesHandler<CMainDlg> 
{
public:
    enum { IDD = IDD_MAINDLG };

    BEGIN_MSG_MAP(CMainDlg)
        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
        COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
        COMMAND_ID_HANDLER(IDOK, OnOK)
        COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
        // Send WM_DROPFILES to its handler...
        CHAIN_MSG_MAP(CDropFilesHandler<CMainDlg>)
    END_MSG_MAP()
    

    LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
      LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        // center the dialog on the screen
        CenterWindow();

        // set icons
        HICON hIcon = (HICON)::LoadImage(
          _Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), 
            IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), 
           ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
        SetIcon(hIcon, TRUE);
        HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), 
            MAKEINTRESOURCE(IDR_MAINFRAME), 
            IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), 
           ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
        SetIcon(hIconSmall, FALSE);
        // The sample dialog application just adds all
        // dropped files to a listbox.
        m_ListBox.Attach(GetDlgItem(IDC_LISTFILES));
        return TRUE;
    }
    // These were left as the wizard created them...
    LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, 
         HWND /*hWndCtl*/, BOOL& /*bHandled*/);
    LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID,
         HWND /*hWndCtl*/, BOOL& /*bHandled*/);
    LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, 
         HWND /*hWndCtl*/, BOOL& /*bHandled*/);
    
    /////////////////////////////////////////////////////////////////////
    //////////////////////// CDropFilesHandler requirements' implementation.
    // In this particular example, we'll put all dropped files in a listbox.
    CListBox m_ListBox;

    BOOL IsReadyForDrop(void)     { m_ListBox.ResetContent(); return TRUE; }
    BOOL HandleDroppedFile(LPCTSTR szBuff)
    {
        ATLTRACE("%s\n", szBuff);
        // In this particular example, we'll do the VERY LEAST possible.
        m_ListBox.AddString(szBuff);

        // Return TRUE unless you're done handling files (e.g., if you want 
        // to handle only the first relevant file, 
        // and you have already found it).
        return TRUE;
    }
    void EndDropFiles(void)
    {
        // Sometimes, if your file handling is not trivial,  
        // you might want to add all
        // file names to some container (std::list<CString> comes to mind), 
        // and do the 
        // handling afterwards, in a worker thread. 
        // If so, use this function to create your worker thread.


        // In this example, we'll display the total number of files dropped.
        CWindow wnd;
        wnd.Attach(GetDlgItem(IDC_COUNT));
        char fmt[] = "Count of files in the last drop: %d";
        char buff[sizeof(fmt) + 30];
        wsprintf(buff, fmt, m_ListBox.GetCount());
        wnd.SetWindowText(buff);
    }
};

Using CDropFilesHandler<CMyView> (with frame windows):

It is almost the same, except that you don't usually have access to your view through the resource editor. You have (at least) three options: adding the style when the mainframe creates the view (this was done in the sample application), calling RegisterDropHandler() on creation from the view itself (more self-contained), if the view handles WM_CREATE, or even calling ModifyStyleEx(0, WS_EX_ACCEPTFILES) either depending on an initialization parameter or on a menu option, thus letting the user enable/disable file dropping.

Points of Interest

This class might save you writing a couple of dozen lines of code per window, which is not much, but it also centralizes maintenance, which is a desirable thing, and enables a programmer to handle dropped files without learning the API related to WM_DROPFILES, which is a (maybe mixed) blessing. If any of you finds this code helpful or inspiring (as I have found so many CodeProject samples), this article has filled its purpose.

History

  • 2004: February - Created.
  • 2004: April - Added the protected member m_nFiles, which holds, during drop operations, the total number of dropped files, and can be used to display a progress bar. Fixed a bug: EndDropFiles() was called for each file, instead of once per drop.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here