65.9K
CodeProject 正在变化。 阅读更多。
Home

将文件拖放到 WTL 窗口中(简单方法)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (13投票s)

2004 年 2 月 23 日

CPOL

4分钟阅读

viewsIcon

55442

downloadIcon

2038

一个混合类,用于帮助将文件拖放到 WTL 窗口中。

Sample Image (Dialog version)

引言

每天,我们中的许多人都会将文件拖放到窗口中,以便让它们“做”某些事情:编辑器(文本或图形)、文字处理器、电子表格、媒体播放器,甚至 Visual Studio 通常也会打开文件:当将文件拖放到“开始”按钮上时,会创建一个快捷方式;当将文件拖放到工具栏上时,某些实用程序会将快捷方式添加到工具栏。

实现它的 API 方法是,将 WS_EX_ACCEPTFILES 添加到对话框、视图或子窗口的扩展样式中,并处理 WM_DROPFILES

由于大多数 WM_DROPFILES 处理程序都非常相似,因此 WTL 的做法是创建一个混合类来处理这项工作的无聊部分,从它继承,并添加您自己的逻辑。

之后,您可以通过大约十几行代码将 WM_DROPFILES 处理添加到您的窗口中。

背景

WM_DROPFILES 处理程序通常会执行以下操作

  • 测试以查看拖放是否发生在相关窗口的客户区内。
  • 测试以查看应用程序是否已准备好处理拖放文件。
  • 检查拖放了多少个文件。
  • 在一个循环中(因为可以一次拖放多个文件):A. 获取每个拖放文件的名称(提供完全限定的路径)。B. 执行一些特定于应用程序的处理。
  • 释放用于管理拖放操作的内存和资源。
  • 如果需要,执行特定于应用程序的资源清理。

如您所见,大多数功能都是样板:只有用粗体标记的操作是特定于应用程序的。如果您决定使用 CDropFilesHandler,那么您只需要编写这些。

使用代码

基本上有两种类型的窗口会处理拖放文件:对话框和框架窗口。

使用 CDropFilesHandler<CMyDialog>(使用对话框)

  • 在资源编辑器中,在“扩展样式”选项卡中标记复选框“接受文件”(见下图),否则 WM_DROPFILES 消息将永远不会到达。

The sample dialog's properties

  • #include <DropFilesHandler.h>(它应该在您的 WTL\Include\CustomExtensions 文件夹中)。
  • CDropFilesHandler<CMyDialog> 继承您的类(从现在起,CMyDialog)。
  • 通过将 CHAIN_MSG_MAP(CDropFilesHandler<CMyDialog>) 添加到您的消息映射来转发消息。
  • 实现三个函数
    • BOOL IsReadyForDrop(void) - 在进入拖放文件循环之前,为每个拖放操作调用一次。如果您的对话框已准备好接受拖放文件,则返回 TRUE,否则返回 FALSE
    • BOOL HandleDroppedFile(LPCTSTR szBuff) - 这是在循环内调用的函数,每个拖放文件调用一次,它应该返回 FALSE 以中断循环。这个函数可能会实现拖放的实际“处理”(例如,通过将文件的名称粘贴到编辑框中,或者在 SDI/MDI/Multi SDI 应用程序中,通过打开文件)。
    • void EndDropFiles(void) - 此函数在退出循环后调用一次。您可以将其留空内联,但是如果您的文件处理不简单,并且您想一次处理多个文件,那么在循环期间将所有文件的名称放入一个容器中(std::list<WTL::CString> 浮现在脑海中)并在那里启动一个工作线程来真正处理它们可能是一个选项。

这是对话框示例项目中的对话框类(相关部分用粗体显示)

// 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);
    }
};

使用 CDropFilesHandler<CMyView>(使用框架窗口)

它几乎相同,除了您通常无法通过资源编辑器访问您的视图。您有(至少)三个选项:当主框架创建视图时添加样式(这在示例应用程序中完成),在视图本身创建时调用 RegisterDropHandler()(更自包含),如果视图处理 WM_CREATE,或者甚至根据初始化参数或菜单选项调用 ModifyStyleEx(0, WS_EX_ACCEPTFILES),从而让用户启用/禁用文件拖放。

关注点

这个类可能会节省您为每个窗口编写几行代码,这并不多,但它也集中了维护,这是一件可取的事情,并且使程序员能够在不学习与 WM_DROPFILES 相关的 API 的情况下处理拖放文件,这可能是一个(混合的)祝福。如果你们中的任何一个人发现此代码有帮助或启发(正如我发现的许多 CodeProject 示例一样),那么本文已经完成了它的目的。

历史

  • 2004 年:2 月 - 创建。
  • 2004 年:4 月 - 添加了受保护的成员 m_nFiles,它在拖放操作期间保存拖放文件的总数,并可用于显示进度条。修复了一个错误:EndDropFiles() 之前是为每个文件调用的,而不是每次拖放调用一次。
© . All rights reserved.