Click here to Skip to main content
Click here to Skip to main content

Dropping Files into a WTL Window (The Easy Way)

, 20 Apr 2004 CPOL
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Pablo Aliskevicius
Software Developer (Senior)
Israel Israel
Pablo writes code for a living, in C++, C#, and SQL.
 
To make all that work easier, he uses some C++ libraries: STL, ATL & WTL (to write Windows applications), and code generation.
 
Pablo was born in 1963, got married in 1998, and is the proud father of two wonderful girls.
 
Favorite quotes:
"Accident: An inevitable occurrence due to the action of immutable natural laws." (Ambrose Bierce, "The Devil's Dictionary", published in several newspapers between 1881 and 1906).
"You are to act in the light of experience as guided by intelligence" (Rex Stout, "In the Best Families", 1950).
Follow on   Google+   LinkedIn

Comments and Discussions

 
GeneralI like the mixin style [modified] Pinmemberflyingxu1-Feb-09 19:41 
GeneralThank you! PinmemberJohnnyMalaria25-Jan-07 15:01 
Hard to believe I am the first commentator.
 
Thank you very much!
 
I searched high and low for information about WTL and Drag And Drop.
 
Your solution works great.
 
At first, it didn't. I'm using Vista. Then I found this:
 
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=736086&SiteID=1
 
With that extra knowledge, it works a treat.
 
Thanks again!
 
John Miller

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 21 Apr 2004
Article Copyright 2004 by Pablo Aliskevicius
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid