转载请说明原出处,谢谢~~
中秋到了,出去玩了几天。今天把仿酷狗程序做了收尾,已经开发完成了,下一篇博客把完结的情况说一下。在这篇博客里说一下使用OLE为窗体增加文件拖拽的功能。使用播放器,我更喜欢直接拖动音乐文件添加到软件里,所以做这个功能很重要。做OLE拖拽之前学习了两篇文章:
http://www.codeproject.com/Articles/840/How-to-Implement-Drag-and-Drop-Between-Your-Progra%E3%80%91
http://blog.csdn.net/liu4584945/article/details/6205341
先来看一下原酷狗里的文件拖动功能:
可以看到,我拖动音乐文件到软件里,进去音乐列表的范围内就显示可复制的图标,不在范围则显示不可拖拽的图标。
让软件支持文件拖拽有两种方法:OLE拖放和文件管理器拖放。第一种方法通过处理窗体的WM_DROPFILES消息,窗体就可以收到拖放进来的文件名。OLE拖放允许你拖放可同时被保存在剪贴板上的任何数据,并且更加细致的控制拖放过程。第一个是比较简单的也是我之前一直使用的方法,下面相关函数的介绍:
UINT DragQueryFile(HDROP hDrop, UINT iFile, LPTSTR lpszFile, UINT cch)
BOOL DragQueryPoint(HDROP hDrop, LPPOINT lppt);
void DragFinish(HDROP hDrop);
使用这个方法时,在窗体初始化完成后调用函数调用DragAcceptFiles(m_hWnd,TRUE),让窗体可以接收WM_DROPFILES消息。然后在Duilib的窗体类中重写HandleCustomMessage函数,去处理WM_DROPFILES消息,代码如下:
else if(uMsg == WM_DROPFILES) { HDROP hDrop = (HDROP)wParam; TCHAR szFilePathName[_MAX_PATH] = {0}; UINT nNumOfFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); //得到文件个数 for (UINT nIndex=0 ; nIndex< nNumOfFiles; ++nIndex) { DragQueryFile(hDrop, nIndex, szFilePathName, _MAX_PATH); //得到文件名 //获取了文件名,开始处理 } DragFinish(hDrop); }
这样就处理完了,处理WM_DROPFILES消息的方法简单,但是效果比较差,无法动态获取文件在窗体上的坐标,样式也难看一些,拖动时的图标仅仅是一个加号而不是原文件的图标样式。适用于做一些要求简单的文件拖动效果。
接下来说一下OLE文件拖动:
OLE文件拖动属于Windows的外壳扩展编程。我在网上查了一些资料,都是关于MFC下OLE拖放的。最后找到了博客开头起到的文件是介绍win32拖放的。我参考了两篇文章的代码,最终封装为一个DropTargetEx类。但是这样做了之后的确是可以达到拖放效果,但是发现拖放时的图标还仅仅是一个加号,而不像我博客开头贴的原酷狗的图片,是对应的文件的图标。查阅资料后了解需要使用IDropTargetHelper接口,让系统辅助来处理消息,就可以达到漂亮的拖拽效果,具体代码我都写在类里面了。大家可以根据自己的需求来修改。
这里先看一下最终的效果:
这个类可以用于win32工程和duilib工程里,使用方法为,在duilib的窗体类中声明一个拖放类的对象:
CDropTargetEx m_DropTarget; //使窗体支持拖放操作
<span style="font-size:14px;"> m_DropTarget.DragDropRegister(m_hWnd); m_DropTarget.SetHDropCallBack(OnDropFiles);</span>
typedef void (*DROPCALLBACK)(CFrameWnd*, HDROP);回调函数的具体写法和WM_DROPFILES消息处理的方法类似,需要把回调函数声明为窗体类的友元。这样就增加了拖动功能。CDropFileEx类的代码如下:
#ifndef DROP_TARGET_EX_H #define DROP_TARGET_EX_H #include "OleIdl.h" #include "ShObjIdl.h" typedef struct _DRAGDATA { int cfFormat; STGMEDIUM stgMedium; }DRAGDATA,*LPDRAGDATA; typedef void (*DROPCALLBACK)(CFrameWnd*, HDROP); class CDropTargetEx : public IDropTarget { public: CDropTargetEx(CFrameWnd *pMainWnd); virtual ~CDropTargetEx(); BOOL DragDropRegister(HWND hWnd,DWORD AcceptKeyState = MK_LBUTTON); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject); ULONG STDMETHODCALLTYPE AddRef(void); ULONG STDMETHODCALLTYPE Release(void); HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState,POINTL pt, DWORD *pdwEffect); HRESULT STDMETHODCALLTYPE DragEnter(IDataObject * pDataObject,DWORD grfKeyState, POINTL pt, DWORD * pdwEffect); HRESULT STDMETHODCALLTYPE DragLeave(void); HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj,DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect); BOOL GetDragData(IDataObject *pDataObject,FORMATETC cFmt); void SetHDropCallBack(DROPCALLBACK pFun); void ProcessDrop(LPDRAGDATA pDropData/*HDROP hDrop*/); //枚举数据格式的函数,我这里并没有用到,但是将来可能会用,函数目前只枚举了HDROP类型 BOOL EnumDragData(IDataObject *pDataObject); private: CFrameWnd *m_pMainWnd; CDuiRect m_rcList; ULONG tb_RefCount; HWND m_hTargetWnd; DWORD m_AcceptKeyState; bool m_bUseDnDHelper; IDropTargetHelper* m_piDropHelper; DROPCALLBACK m_pDropCallBack; vector<LPDRAGDATA> m_Array; }; #endif //DROP_TARGET_EX_H
#include "duilib.h" CDropTargetEx::CDropTargetEx(CFrameWnd *pMainWnd): m_pMainWnd(pMainWnd), tb_RefCount(0), m_hTargetWnd(0), m_AcceptKeyState(0), m_piDropHelper(NULL), m_bUseDnDHelper(false), m_pDropCallBack(NULL) { // Create an instance of the shell DnD helper object. if ( SUCCEEDED( CoCreateInstance ( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (void**) &m_piDropHelper ) )) { m_bUseDnDHelper = true; } } CDropTargetEx::~CDropTargetEx() { if ( NULL != m_piDropHelper ) m_piDropHelper->Release(); } BOOL CDropTargetEx::DragDropRegister(HWND hWnd, DWORD AcceptKeyState) { if(!IsWindow(hWnd))return false; HRESULT s = ::RegisterDragDrop (hWnd,this); if(SUCCEEDED(s)) { m_hTargetWnd = hWnd; m_AcceptKeyState = AcceptKeyState; if (m_pMainWnd->GetLeftListPos(m_rcList)) return true; return false; } else { return false; } } HRESULT STDMETHODCALLTYPE CDropTargetEx::QueryInterface(REFIID iid, void ** ppvObject) { *ppvObject = NULL; if (iid == IID_IDropTarget) *ppvObject = static_cast<IDropTarget*>(this); if( *ppvObject != NULL ) AddRef(); return *ppvObject == NULL ? E_NOINTERFACE : S_OK; } ULONG STDMETHODCALLTYPE CDropTargetEx::AddRef(void) { InterlockedIncrement(&tb_RefCount); return tb_RefCount; } ULONG STDMETHODCALLTYPE CDropTargetEx::Release(void) { ULONG ulRefCount = InterlockedDecrement(&tb_RefCount); return ulRefCount; } HRESULT STDMETHODCALLTYPE CDropTargetEx::DragOver(DWORD grfKeyState,POINTL pt, DWORD *pdwEffect) { ScreenToClient(m_hTargetWnd, (LPPOINT)&pt); if( grfKeyState != m_AcceptKeyState || pt.x < m_rcList.left || pt.x > m_rcList.right || pt.y < m_rcList.top || pt.y > m_rcList.bottom) { *pdwEffect = DROPEFFECT_NONE; } else { *pdwEffect = DROPEFFECT_COPY ; } if ( m_bUseDnDHelper ) { m_piDropHelper->DragOver((LPPOINT)&pt, *pdwEffect); } return S_OK; } HRESULT STDMETHODCALLTYPE CDropTargetEx::DragEnter(IDataObject * pDataObject,DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) { if( grfKeyState != m_AcceptKeyState ) { *pdwEffect = DROPEFFECT_NONE; return S_OK; } //我这里只关心CE_HDROP类型,如果需要,可以调用EnumDragData函数来枚举所有类型 FORMATETC cFmt = {(CLIPFORMAT) CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; GetDragData(pDataObject, cFmt); *pdwEffect = DROPEFFECT_COPY; if ( m_bUseDnDHelper ) { m_piDropHelper->DragEnter ( m_hTargetWnd, pDataObject, (LPPOINT)&pt, *pdwEffect ); } return S_OK; } HRESULT STDMETHODCALLTYPE CDropTargetEx::DragLeave(void) { int temp = m_Array.size(); for(UINT i = 0;i < m_Array.size(); i++) { LPDRAGDATA pData = m_Array[i]; ::ReleaseStgMedium(&pData->stgMedium); delete pData; m_Array.clear(); } if ( m_bUseDnDHelper ) { m_piDropHelper->DragLeave(); } return S_OK; } HRESULT STDMETHODCALLTYPE CDropTargetEx::Drop(IDataObject *pDataObj,DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) { int temp = m_Array.size(); for(UINT i = 0; i < m_Array.size(); i++) { LPDRAGDATA pData = m_Array[i]; //我这里只获取了HDROP类型的数据,所以直接开始处理 ProcessDrop(pData); ::ReleaseStgMedium(&pData->stgMedium); delete pData; m_Array.clear(); } if ( m_bUseDnDHelper ) { m_piDropHelper->Drop ( pDataObj, (LPPOINT)&pt, *pdwEffect ); } return S_OK; } BOOL CDropTargetEx::EnumDragData(IDataObject *pDataObject) { IEnumFORMATETC *pEnumFmt = NULL; //如果获取成功,则可以通过IEnumFORMATETC接口的Next方法,来枚举所有的数据格式: HRESULT ret = pDataObject->EnumFormatEtc (DATADIR_GET,&pEnumFmt); pEnumFmt->Reset (); HRESULT Ret = S_OK; FORMATETC cFmt = {0}; ULONG Fetched = 0; while(Ret != S_OK) { Ret = pEnumFmt->Next(1,&cFmt,&Fetched); if(SUCCEEDED(ret)) { if( cFmt.cfFormat == CF_HDROP) { if(GetDragData(pDataObject,cFmt)) return TRUE; } } else { return FALSE; } } return TRUE; } BOOL CDropTargetEx::GetDragData(IDataObject *pDataObject,FORMATETC cFmt) { HRESULT ret=S_OK; STGMEDIUM stgMedium; ret = pDataObject->GetData(&cFmt, &stgMedium); if (FAILED(ret)) return FALSE; if (stgMedium.pUnkForRelease != NULL) return FALSE; switch (stgMedium.tymed) { //可以扩充这块代码,配合EnumDragData函数来保存更多类型的数据 case TYMED_HGLOBAL: { LPDRAGDATA pData = new DRAGDATA; pData->cfFormat = cFmt.cfFormat ; memcpy(&pData->stgMedium,&stgMedium,sizeof(STGMEDIUM)); m_Array.push_back(pData); return true; break; } default: // type not supported, so return error { ::ReleaseStgMedium(&stgMedium); } break; } return false; } void CDropTargetEx::SetHDropCallBack(DROPCALLBACK pFun) { if (pFun != NULL) { m_pDropCallBack = pFun; } } void CDropTargetEx::ProcessDrop(LPDRAGDATA pDropData/*HDROP hDrop*/) { switch(pDropData->cfFormat) { case CF_TEXT: { //m_pTextCallBack((HDROP)pDropData->stgMedium.hGlobal); break; } case CF_HDROP: { m_pDropCallBack(m_pMainWnd, (HDROP)pDropData->stgMedium.hGlobal); break; } default: break; } }
CDropTargetEx类的下载地址为:点击打开链接
目前只是根据我的需求编写 CDropTargetEx类,实际上还可以扩充来完成更多功能。
Redrain 2014.9.9