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






4.81/5 (13投票s)
一个混合类,用于帮助将文件拖放到 WTL 窗口中。
引言
每天,我们中的许多人都会将文件拖放到窗口中,以便让它们“做”某些事情:编辑器(文本或图形)、文字处理器、电子表格、媒体播放器,甚至 Visual Studio 通常也会打开文件:当将文件拖放到“开始”按钮上时,会创建一个快捷方式;当将文件拖放到工具栏上时,某些实用程序会将快捷方式添加到工具栏。
实现它的 API 方法是,将 WS_EX_ACCEPTFILES
添加到对话框、视图或子窗口的扩展样式中,并处理 WM_DROPFILES
。
由于大多数 WM_DROPFILES
处理程序都非常相似,因此 WTL 的做法是创建一个混合类来处理这项工作的无聊部分,从它继承,并添加您自己的逻辑。
之后,您可以通过大约十几行代码将 WM_DROPFILES
处理添加到您的窗口中。
背景
WM_DROPFILES
处理程序通常会执行以下操作
- 测试以查看拖放是否发生在相关窗口的客户区内。
- 测试以查看应用程序是否已准备好处理拖放文件。
- 检查拖放了多少个文件。
- 在一个循环中(因为可以一次拖放多个文件):A. 获取每个拖放文件的名称(提供完全限定的路径)。B. 执行一些特定于应用程序的处理。
- 释放用于管理拖放操作的内存和资源。
- 如果需要,执行特定于应用程序的资源清理。
如您所见,大多数功能都是样板:只有用粗体标记的操作是特定于应用程序的。如果您决定使用 CDropFilesHandler
,那么您只需要编写这些。
使用代码
基本上有两种类型的窗口会处理拖放文件:对话框和框架窗口。
使用 CDropFilesHandler<CMyDialog>(使用对话框)
- 在资源编辑器中,在“扩展样式”选项卡中标记复选框“接受文件”(见下图),否则
WM_DROPFILES
消息将永远不会到达。
#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()
之前是为每个文件调用的,而不是每次拖放调用一次。