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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.12/5 (14投票s)

2003 年 4 月 6 日

4分钟阅读

viewsIcon

91889

downloadIcon

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

在应用程序中接收文件

为了使用拖放技术接收文件,我使用了OnDragOverOnDrop通知。在OnDragOver函数中,我判断是文件列表还是其他东西被拖到窗口上方,并返回操作的预期结果。如果不是文件,返回值为DROPEFFECT_NONE。这允许系统设置正确的鼠标光标。在OnDrop函数中,进行文件提取和缓冲区清空。为了让这些函数工作,窗口应该被注册为拖放操作的处理程序。注册可以使用COleDropTarget对象,如下所示:

void CFilesDragDropView::OnInitialUpdate()
{
...
VERIFY( m_DropTarget.Register(this) );
...
}

剪贴板和拖放使用相同的数据传输结构。在处理OnPasteOnDrop消息时,会从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函数创建必要的数据结构。通过GetBufferGetBuffSize函数访问该结构。在使用复制或剪切命令时,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应用程序,然后再从该应用程序传输到另一个目录,文件将被实际传输。因此,在测试时,我建议使用特殊的临时目录,以避免传输不需要的文件。

链接

© . All rights reserved.