现在有很多软件都支持拖拽功能,就是用鼠标拖着文件放到软件窗口中某个位置,软件就可以做出相应的反应。比如,Windows自带的播放软件Windows Media Player,你可以拖着一个媒体文件放到它的主窗口中,它就会开始播放这个文件。
怎么让自己的软件也有这个功能呢?
其实,并不是很难,只要让你需要有拖拽功能的控件响应WM_DROPFILES消息就行了。下面以Edit控件和ListBox控件为例子来说明。
首先,从CEdit继承,自定义一个Edit控件。在头文件添加如下的消息响应函数:
MyEdit.h
#pragma once // CMyEdit class CMyEdit : public CEdit { DECLARE_DYNAMIC(CMyEdit) public: CMyEdit(); virtual ~CMyEdit(); protected: DECLARE_MESSAGE_MAP() public: afx_msg void OnDropFiles(HDROP hDropInfo); };
然后,在cpp文件中的消息映射中添加:
最后,在cpp文件中添加OnDropFiles函数的实现,对拖拽的文件进行处理。下面是完整的代码:
MyEdit.cpp
// MyEdit.cpp : 实现文件 // #include "stdafx.h" #include "PEinfo.h" #include "MyEdit.h" // CMyEdit IMPLEMENT_DYNAMIC(CMyEdit, CEdit) CMyEdit::CMyEdit() { } CMyEdit::~CMyEdit() { } BEGIN_MESSAGE_MAP(CMyEdit, CEdit) ON_WM_DROPFILES() END_MESSAGE_MAP() // CMyEdit 消息处理程序 void CMyEdit::OnDropFiles(HDROP hDropInfo) { if(hDropInfo) { int nDrag; //拖拽文件的数量 nDrag = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); if(nDrag == 1) {
// 被拖拽的文件的文件名 TCHAR Path[MAX_PATH+1] = {0}; int len;
// 得到被拖拽的文件名 len = DragQueryFile(hDropInfo, 0, Path, MAX_PATH);
// 把文件名显示出来 SetWindowText(Path); } else { MessageBox(_T("只能拖拽一个PE文件!!")); } } //CMyEdit::OnDropFiles(hDropInfo); }
PEinfoDlg.h
#include "MyEdit.h" public: CMyEdit m_Edit;
PEinfoDlg.cpp
void CPEinfoDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST_SECTION_TABLE, m_CListCtrl); DDX_Control(pDX, ID_EDIT_FILEPATH, m_Edit); //映射成自己继承的CMyEdit }
可以看到,在上面的代码中,得到拖拽文件名的是函数DragQueryFile。这是Windows提供的一个API函数,用来得到一次成功的拖拽操作中的文件名。下面是该函数的原型:
// 参数说明:
UINT DragQueryFile(HDROP hDrop, UINT iFile, LPTSTR lpszFile, UINT cch);
// hDrop 句柄,由消息传入。 // iFile 文件索引。 // 如果该参数值为0xFFFFFFFF,函数返回拖拽操作中的文件数量。 // 如果该参数值为0到拖拽文件数量之间的数值,函数将对应索引的文件名拷贝到参数lpszFile所指的字符串内。 // lpszFile 存放文件名的字符串。如果该参数为NULL,函数返回需要的字符串长度。 // cch 存放文件名的字符串的长度。
从上面对函数参数的说明可以看出,该函数支持一次多选多个文件。就像Windows Media Player一样,一次多选好几个文件拖拽到它的窗口中,它就会按顺序播放文件。下面我就用一个有拖拽功能的ListBox为例,说明怎么处理一次拖拽多个文件。
// lxListBox.h
// lxListBox.cpp
class ClxListBox : public CListBox { DECLARE_DYNAMIC(ClxListBox) public: ClxListBox(); virtual ~ClxListBox(); afx_msg void OnDropFiles(HDROP hDropInfo); protected: DECLARE_MESSAGE_MAP() };
#include "stdafx.h" #include "lxListBox.h" IMPLEMENT_DYNAMIC(ClxListBox, CListBox) ClxListBox::ClxListBox() { } ClxListBox::~ClxListBox() { } BEGIN_MESSAGE_MAP(ClxListBox, CListBox) ON_WM_DROPFILES() END_MESSAGE_MAP() void ClxListBox::OnDropFiles(HDROP hDropInfo) { char szFileName[MAX_PATH]; int iFileNumber; // 得到拖拽操作中的文件个数 iFileNumber = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); for (int i = 0; i < iFileNumber; i++) { // 得到每个文件名 DragQueryFile(hDropInfo, i, szFileName, MAX_PATH); if (FindString(0, szFileName) != LB_ERR) continue; // 把文件名添加到list中 AddString(szFileName); } CListBox::OnDropFiles(hDropInfo); }
补充一下,函数DragQueryFile得到的文件名,是带完整路径的文件名。
最后,提醒一下:要想你的控件具有拖拽功能,一定要把它的Accept Files属性设置为True!要不,上面写的那些代码是不起作用的!