如何实现程序与浏览器之间的拖放(转)
来源:互联网 发布:百度搜索for mac 编辑:程序博客网 时间:2024/04/27 00:34
如何实现程序与浏览器之间的拖放(一)2008-12-27 13:48
http://hi.baidu.com/aoca/blog/item/aa8c602a933b6d3f5243c12f.html
翻译:How to Implement Drag and Drop Between Your Program and Explorer
介绍
最近在CodeProject留言板,看到许多关于应用程序与浏览器之间的拖放问题。如同许多Windows编程一样,了解之后就很容易实现,但是寻找答案是一件很烦人的事情。在这篇文章里,我将演示如何应用拖放。让你的应用程序与浏览器之间实现拖放文件。
例子是MFC程序,所以在这里假设你熟悉C++,MFC,并且知道使用COM对象和接口。如果你想先熟悉COM对象和接口,请参考我的文章Intro to COM。MultiFiler是一个类似“文件中转站”的小程序。你可以拖拽任意多个文件到MultiFiler,它将文件列举在列表里。你也可以将MultiFiler里的文件拖回浏览器,配合使用Shift和Ctrl键,复制或移动文件。
与浏览器的拖拽
你知道,浏览器可以让你在浏览器或桌面之间拖拽文件。当你拖拽文件时,浏览器窗口(源窗口),会创建一个实现了IDataObject接口的对象,并在其中放入一些数据。接收文件的窗口(目标窗口),使用IDataObject的函数读取这些数据;这就是为什么它知道有文件拖进来。
如果你使用ClipSpy来查看包含在IDataObject中的数据,你可以看到浏览器在数据对象中放置了这些信息:
其中最重要的是CF_HDROP。其他的信息是浏览器给自己使用的。如果我们将应用程序注册成拖拽目标,并且知道如何读取CF_HDROP,那么就可以接收拖拽的文件了。类似的,如果知道如何将数据放入CF_HROP,我们的应用程序就可以成为拖拽源。那么,CF_HDROP里面有些什么数据呢?请继续往下读…
DROPFILES数据结构
那么CF_HDROP具体是什么呢?它是一个DROPFILES结构体。另外有一个HDROP类型,它是一个DROPFILES结构体的指针。
DROPFILES并不复杂。它的定义如下:
struct DROPFILES
{
DWORD pFiles; // offset of file list
POINT pt; // drop point (client coords)
BOOL fNC; // is it on NonClient area and pt is in screen coords
BOOL fWide; // wide character flag
};
其中文件名列表不在结构体的定义中。它是以双null结尾的字符串列表。可是它存放在哪里呢?它紧接着fWide字段存放,pFiles保存它在内存中的位移量(相对于结构体的起始位置)。另外只有fWide在拖拽中使用,它表示文件名是ANSI还是Unicode字符。
接收来自浏览器的拖拽对象
接收拖拽对象要比创建一个简单,所以我们先接受。
在应用程序中接受拖拽对象有2种方式。第一种使用WM_DROPFILES消息,Window 3.1时候的产物。另一种是将你的应用程序注册为OLE拖拽目标。
传统的方式-WM_DROPFILES
要使用传统的方法,首先设置窗口的“accept files”样式。对于对话框,在“Extended Styles”标签里设置,如下截图:
你可以通过DragAcceptFiles() API在运行期设置,此方法接受2个参数。第一个是主窗口的句柄,第二个设置成TRUE,表明接受拖拽对象。如果你的窗口是CView而非对话框,你只能在运行期设置。
不管你使用哪种方式,你的窗口都会变成拖拽目标。当你从浏览器拖拽文件或文件夹到自己的窗口时,你会收到WM_DROPFILES消息。WM_DROPFILES消息的WPARAM参数是HDROP,它包含了被拖拽文件的列表。有3个API用于取出HDROP里的文件类表:DragQueryFile(), DragQueryPoint(), 和 DragFinish().
DragQueryFile()处理2件事情:返回被拖拽文件的数量,还有遍历文件列表。DragQueryPoint()返回DROPFILES结构体的pt字段。DragFinish()清理拖拽过程分配的内存。
DragQueryFile()接收4个参数:HDROP,文件名的索引,用于保存文件名的缓存,还有缓存的字符长度。如果你将-1作为索引传入,DragQueryFile()将返回文件列表的长度。否则返回文件名的字符数。将返回值与0比较,来判断函数是否执行成功。
DragQueryPoint()接收2个参数,HDROP,和用于保存DROPFILES结构体pt字段的POINT结构体指针。DragFinish()只接受一个HDROP参数。
WM_DROPFILES的处理函数如下:
void CMyDlg::OnDropFiles ( HDROP hdrop )
{
UINT uNumFiles;
TCHAR szNextFile [MAX_PATH];
// Get the # of files being dropped.
uNumFiles = DragQueryFile ( hdrop, -1, NULL, 0 );
for ( UINT uFile = 0; uFile < uNumFiles; uFile++ )
{
// Get the next filename from the HDROP info.
if ( DragQueryFile ( hdrop, uFile, szNextFile, MAX_PATH ) > 0 )
{
// ***
// Do whatever you want with the filename in szNextFile.
// ***
}
}
// Free up memory.
DragFinish ( hdrop );
}
如果你只是想获得文件列表,不会用到DragQueryPoint()函数(事实上,我们基本不会使用)。
新的方法-OLE拖拽目标
另一个接受拖放对象的方式,就是将你的窗口注册成OLE拖拽目标。通常情况下,使用这种方式,你必须编写一个实现IDropTarget接口的C++类。好在有MFC的COleDropTarget类减轻我们的负担。根据主窗口是CView,还是对话框,实现过程有一些不同,我将分别说明。
将CView作为拖拽目标
CView已经支持拖拽,只是没有激活。要激活它,你需要添加一个COleDropTarget成员变量,然后在视图的OnInitialUpdate()中调用Register(),使其变成拖拽目标,代码如下:
void CMyView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// Register our view as a drop target.
// m_droptarget is a COleDropTarget member of CMyView.
m_droptarget.Register ( this );
}
然后你重载4个虚函数,当用户拖拽文件经过你的视图时,这些函数会被调用:
OnDragEnter():当鼠标进入你的窗口时调用。
OnDragOver():当鼠标在窗口内移动时调用。
OnDragLeave():当鼠标移除你的窗口时调用。
OnDrop():当用户把文件放入你的窗口时调用。
OnDragEnter()
OnDragEnter()是第一个被调用的函数。它的原型是:
DROPEFFECT CView::OnDragEnter( COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point );
参数分别是:
pDataObject:指向包含拖拽信息的COleDataObject对象指针。
dwKeyState:一些标志,表明哪些鼠标按键,哪些键盘按键被点击。相应的标志是MK_CONTROL, MK_SHIFT, MK_ALT, MK_LBUTTON, MK_MBUTTON, 和 MK_RBUTTON。
point:鼠标指针位置,相对视图区域的坐标。
OnDragEnter()返回DROPEFFECT值,用来告诉OLE,此拖放是否被接受,如果接受,显示哪种鼠标样式。样式值的意义是:
DROPEFFECT_NONE:不支持拖拽,鼠标变成:
DROPEFFECT_MOVE:数据将被移动到拖拽目标。鼠标变成:
DROPEFFECT_COPY:数据将被复制到拖拽目标。鼠标变成:
DROPEFFECT_LINK:数据将在拖拽目标中创建链接。鼠标变成:
通常在OnDragEnter()中验证被拖拽的数据是否是你的程序所支持的。如果不是,返回DROPEFFECT_NONE来拒绝拖拽对象。此外,根据你的意图返回相应的值。
OnDragOver()
如果你在OnDragEnter()返回的不是DROPEFFECT_NONE,鼠标进入你的窗口后,OnDragOver()函数会被调用。OnDragOver()的原型如下:
DROPEFFECT CView::OnDragOver ( COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point );
参数,返回值与OnDragEnter()一致。OnDragOver()可以让你根据鼠标所在的不同区域,按键情况返回不同的DROPEFFECT。例如,你的窗口有多个区域,显示不同信息,但是你只允许其中一个区域接受拖拽,通过检查鼠标位置的参数,当不在接受数据区域时返回DROPEFFECT_NONE。
对于组合键,通常使用如下的处理方式:
SHIFT按下(dwKeyState值为MK_SHIFT):返回DROPEFFECT_MOVE。
CONTROL按下(MK_CONTROL):返回DROPEFFECT_COPY。
同时按下(MK_SHIFT | MK_CONTROL):返回DROPEFFECT_LINK。
那些只是规范,但是最好遵守它,因为浏览器也是这样做的。如果某些操作(复制,移动,链接)在你的程序里没有意义,你不需要返回相应的DROPEFFECT。例如,在MultiFiler(我保证我会修改的)中OnDragOver()只返回DROPEFFECT_COPY。只要取保返回正确的值,这样鼠标就可以正确表现程序的操作。
OnDragLeave()
当拖拽移除你的窗口又没有放手时会被调用。它的原型是:
void CView::OnDragLeave();
它没有参数,也没有返回值,目的只是让你清理在OnDragEnter() 和 OnDragOver()中使用的资源。
OnDrop()
如果用户将对象放入你的窗口(并且你没有在最后一次的OnDragOver()返回DROPEFFECT_NONE),这是OnDrop()函数会被调用。它的原型是:
BOOL CView::OnDrop ( COleDataObject* pDataObject,
DROPEFFECT dropEffect, CPoint point );
dropEffect参数是最后一次OnDragOver()的返回值,其它的与OnDragEnter()一致。如果操作成功(根据你自己对成功的定义)返回TRUE,否则返回FALSE。
OnDrop()是具体操作的地方-你可以根据数据和程序本来处理。在MultiFiler中,拖放的文嘉会加到窗口的列表控件中。
让对话框成为拖拽目标
如果你的主窗口是对话框(或是非派生至CView),事情会变得复杂一些。因为COleDropTarget的设计意图就是为了让CView派生的窗口支持拖放,所以你需要创建一个新类,继承COleDropTarget,并重写之前的4个函数。
派生于COleDropTarget的类,通常像这样:
class CMyDropTarget : public COleDropTarget
{
public:
DROPEFFECT OnDragEnter ( CWnd* pWnd, COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point );
DROPEFFECT OnDragOver ( CWnd* pWnd, COleDataObject* pDataObject,
DWORD dwKeyState, CPoint point );
BOOL OnDrop ( CWnd* pWnd, COleDataObject* pDataObject,
DROPEFFECT dropEffect, CPoint point );
void OnDragLeave ( CWnd* pWnd );
CMyDropTarget ( CMyDialog* pMainWnd );
virtual ~CMyDropTarget();
protected:
CMyDialog* m_pParentDlg; // initialized in constructor
};
在这个例子里,构造器中传入主窗口的指针,这样可以在拖拽函数里像主窗口发送消息,或其他的事情。你也可以按照自己的意愿修改构造器。然后按照前面的描述实现那4个函数。不同的是CWnd*参数,它是指向鼠标经过的窗口指针。
当你完成这个类的编写后,在对话框里加入此变量,然后在OnInitDialog()中调用Register()函数:
BOOL CMyDialog::OnInitDialog()
{
// Register our dialog as a drop target.
// m_droptarget is a CMyDropTarget member of CMyDialog.
m_droptarget.Register ( this );
}
访问CDataObject的HDROP
如果你使用OLE拖拽对象,你的函数会收到有一个指向COleDataObject的指针。它是一个实现了IDataObject的MFC类,包含了拖拽源所创建的全部数据。通过代码先获取CF_HDROP,再得到HDROP句柄。得到HDROP后,就可以像前面说的那样使用DragQueryFile()获取拖拽文件列表。
这里是获取HDROP的代码:
BOOL CMyDropTarget::OnDrop ( CWnd* pWnd, COleDataObject* pDataObject,
DROPEFFECT dropEffect, CPoint point )
{
HGLOBAL hg;
HDROP hdrop;
// Get the HDROP data from the data object.
hg = pDataObject->GetGlobalData ( CF_HDROP );
if ( NULL == hg )
return FALSE;
hdrop = (HDROP) GlobalLock ( hg );
if ( NULL == hdrop )
{
GlobalUnlock ( hg );
return FALSE;
}
// Read in the list of files here...
GlobalUnlock ( hg );
// Return TRUE/FALSE to indicate success/failure
}
总结2种方式
处理 WM_DROPFILES消息:
从Windows 3.1保留而来;可能在将来被去除。
无法定制拖拽的过程,你只能在拖拽完成时收到消息。
无法看到拖拽的原始数据。
如果你不需要定制效果,这个方式比较容易代码。
使用OLE拖拽目标:
使用COM接口,比较新的方式。
有MFC的CView和COleDropTarget支持。
控制整个拖拽的过程。
通过IDataObject得到所有的数据。
需要编写更多代码,一旦你完成,这些代码可以复制粘贴到别的项目。
- 如何实现程序与浏览器之间的拖放(转)
- VirtualBox实现虚拟机与主机之间的文件拖放
- ListView之间的拖放功能的实现
- [转]如何在 JavaScript 中实现拖放
- [转]如何在 JavaScript 中实现拖放
- [转]如何在 JavaScript 中实现拖放
- 如何实现浏览器内多个标签页之间的通信
- 如何实现浏览器内多个标签页之间的通信
- 如何实现浏览器内多个标签页之间的通信?
- 如何实现浏览器内多个标签页之间的通信?
- 如何实现浏览器内多个标签页之间的通信
- 如何实现浏览器多个标签页之间的通信?
- 如何实现浏览器内多个标签页之间的通信?
- 利用浏览器实现程序界面与实现的分离
- 利用浏览器实现程序界面与实现的分离
- 利用浏览器实现程序界面与实现的分离
- 利用浏览器实现程序界面与实现的分离
- 如何实现鼠标拖放文件
- Adobe AIR For Dummies
- 腾中重工洽购悍马揭秘:低调富豪李炎浮现
- 用commons-fileupload-1.2 实现文件上传
- 2009年6月4日
- config文件更改
- 如何实现程序与浏览器之间的拖放(转)
- 如何卸载微软反盗版补丁(KB905474)的解决办法
- 如何去除mysql中的auto_increment为1
- 转自专业论坛-微软自曝防止黑屏绝招:卸载组件可高枕无忧
- 虚假的测试繁荣-Zee
- 应聘某公司VC实习生,结果要我完成这些题
- 今天的开始
- Tomcat 6.0 配置数据库连接池(oracle,mysql,sqlserver)
- 学习Linux