使用MFC在文件列表上进行拖放、剪切、复制和粘贴操作






4.12/5 (14投票s)
2003 年 4 月 6 日
4分钟阅读

91889

2860
本文解释了如何使用MFC在文件列表上执行拖放、剪切、复制和粘贴操作。
引言
在本文中,实际描述了将一组文件从一个应用程序移动到另一个应用程序的几乎所有操作。我考虑了接收文件到当前应用程序以及从当前应用程序传输文件到其他程序。本文描述了拖放、剪切、复制、粘贴操作。并通过演示项目展示了技术。
在开发程序Mp3 Music Explorer时,我遇到了一个问题:在微软的文档以及知名的软件开发网站上,都没有一个展示文件移动所有必要操作的示例。因此,我创建了一个名为FilesDragDrop的演示项目。它基于MFC SDI项目。文件显示使用了CListView
类。
有两种方式可以在应用程序中接收文件拖放:使用WM_DROPFILES
消息和使用OLE机制。这两种方式,以及通过剪贴板传输文件,都使用同一个文件描述结构DROPFILES
。从应用程序拖放文件仅使用OLE机制。因此,我使用OLE技术实现了所有文件移动操作。
初始化
文件移动的实现可以在没有OLE服务器和OLE客户端的概念下完成。它们对于这个任务来说是多余的。可以使用普通的MFC项目。但是为了让必要的OLE类工作,我们需要初始化OLE库。为此,在CFilesDragDropApp::InitInstance()
函数中插入以下代码行:
// Initialize OLE 2.0 libraries if (!AfxOleInit()) { AfxMessageBox("AfxOleInit Error!"); return FALSE; }
在应用程序中接收文件
为了使用拖放技术接收文件,我使用了OnDragOver
和OnDrop
通知。在OnDragOver
函数中,我判断是文件列表还是其他东西被拖到窗口上方,并返回操作的预期结果。如果不是文件,返回值为DROPEFFECT_NONE
。这允许系统设置正确的鼠标光标。在OnDrop
函数中,进行文件提取和缓冲区清空。为了让这些函数工作,窗口应该被注册为拖放操作的处理程序。注册可以使用COleDropTarget
对象,如下所示:
void CFilesDragDropView::OnInitialUpdate() { ... VERIFY( m_DropTarget.Register(this) ); ... }
剪贴板和拖放使用相同的数据传输结构。在处理OnPaste
和OnDrop
消息时,会从COleDataObject
对象中提取文件列表。
void CFilesDragDropView::OnEditPaste() { COleDataObject DataObject; if( DataObject.AttachClipboard() ) { DataObjectToList(&DataObject); } } BOOL CFilesDragDropView::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) { BOOL bRet = DataObjectToList(pDataObject); ... }
用于传输文件的__数据类型__是CF_HDROP
。数据通过全局内存传递,其句柄可以从COleDataObject
使用GetData
函数获取。传输文件的数量和路径可以通过句柄使用DragQueryFile
函数(CFilesDragDropView::FileNamesToList
)获得。接收到的文件显示在ListView
窗口中。
从应用程序传输文件
要使用复制/粘贴和拖放技术从应用程序传输文件,需要创建DROPFILES
结构。该结构对应于交换缓冲区__数据类型__CF_HDROP
。结构中的文件路径之间用'\0'符号分隔,文件列表的末尾用两个'\0'符号标记。有一个标准的从缓冲区提取路径的函数——DragQueryFile
,但没有用于创建缓冲区的标准函数。为此,我设计了一个名为CDropFiles
的类。该类按如下方式使用:
- 第一个函数
AddFile
会输入所有传输文件的路径。 - 然后
CreateBuffer
函数创建必要的数据结构。通过GetBuffer
和GetBuffSize
函数访问该结构。在使用复制或剪切命令时,CDropFile::m_pBuff
中生成的数据通过SetClipboardData
函数被输入到应用程序之间的交换缓冲区。
在拖放技术中,从应用程序传输文件使用OnBeginDrag
函数。它由CListCtrl
类的LVN_BEGINDRAG
消息调用。如果我们使用的是一个不生成此类消息的窗口,可以使用WM_LBUTTONDOWN
消息。通过CacheGlobalData
函数将传输数据输入COleDataSource
对象,并通过DoDragDrop
函数进行数据传输。为了让COleDataSource
对象工作,需要创建一个COleDropSource
对象。此对象在传输中不以任何方式使用。它在构造函数中执行所有必要的操作。交换缓冲区的内存由接收方清空。
函数文本
void CFilesDragDropView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; CDropFiles DropFiles; // My class for creating DROPFILES struct if(!PrepareFileBuff(DropFiles)) { ASSERT(0); } COleDropSource DropSource; COleDataSource DropData; HGLOBAL hMem = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE, DropFiles.GetBuffSize()); memcpy( (char*)::GlobalLock(hMem), DropFiles.GetBuffer(), DropFiles.GetBuffSize() ); ::GlobalUnlock(hMem); DropData.CacheGlobalData( CF_HDROP, hMem ); DROPEFFECT de = DropData.DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE,NULL); if(de == DROPEFFECT_COPY) { // Copy files if CTRL btn is hold; } else { // Move files, as default; DeleteSelectedFiles(); } *pResult = 0; }
演示项目测试
如果将文件从任何目录传输到FilesDragDrop应用程序,然后再从该应用程序传输到另一个目录,文件将被实际传输。因此,在测试时,我建议使用特殊的临时目录,以避免传输不需要的文件。
链接
- http://www.brigsoft.com/mp3explorer - 本文所述技术已应用于产品Mp3 Music Explorer。
- http://www.brigsoft.com/edu/FilesDragDrop/FilesDragDrop_demo.zip - 演示项目 (VC++ v.6)
- http://www.brigsoft.com/edu - 其他作者的文章和源代码。