在ActiveX中实现Drag&Drop的方法
Q:
如何将本地文件拖放入在网页上的ActiveX中?
A:
在ATL实现的ActiveX中,继承IDropTarget接口。
在MFC实现的ActiveX中的方法,实现一个窗口CWnd子类,在ActiveX Control创建的时候创建在ActiveX界面上面。然后用这个窗口响应Drag&Drop消息。
int CTestCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (COleControl::OnCreate(lpCreateStruct) == -1) return -1;
m_wndTest = new CTestWnd();
CRect rcClient(lpCreateStruct->x, lpCreateStruct->y,
lpCreateStruct->x + lpCreateStruct->cx,
lpCreateStruct->y + lpCreateStruct->cy);
if(m_wndTest->Create(rcClient, this) == -1) return -1;
return 0;
}
其实可以在CTestCtrl中直接Regist,创建CWnd窗口的好处是可以简单的响应很多消息。
处理FileDrop的一种方法,注册响应WM_DROPFILES消息:
int CTestWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (CWnd::OnCreate(lpCreateStruct) == -1) return -1;
ModifyStyleEx(0, WS_EX_ACCEPTFILES);
...
}
然后在窗口中实现WM_DROPFILES消息的处理函数afx_msg void OnDropFiles(HDROP hDropInfo);处理方法参考msdn或者后面的OnDrop函数代码。
另一种方法,写一个COleDropTarget的子类。
class CTestTgt : public COleDropTarget {
// 注,此处可以写成Template,只是懒得写一个接口了。
public:
CTestTgt():m_wndForward(NULL){};
BOOL Register(CTestWnd *const forward){
// 注,此处可以做一个队列,挨个通知
if(m_wndForward != NULL) return FALSE;
m_wndForward = forward;
return COleDropTarget::Register(forward);
}
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) {
if(m_wndForward == NULL) return DROPEFFECT_NONE;
return m_wndForward->OnDragEnter(pDataObject);
}
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) {
if(m_wndForward == NULL) return DROPEFFECT_NONE;
return m_wndForward->OnDragOver(pDataObject);
}
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) {
if(m_wndForward == NULL) return DROPEFFECT_NONE;
return m_wndForward->OnDrop(pDataObject);
}
virtual void OnDragLeave(CWnd* pWnd) {
return m_wndForward->OnDragLeave(pDataObject);
}
protected:
CTestWnd* m_wndForward;
};
在CTestWnd::OnCreate方法中:
int CTestWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (CWnd::OnCreate(lpCreateStruct) == -1) return -1;
m_drpTarget.Register(this);
...
}CTestWnd实现:
DROPEFFECT CTestWnd::OnDragEnter(COleDataObject* pDataObject) {
DROPEFFECT de = DROPEFFECT_NONE;
m_bDragOver = TRUE;
if(!pDataObject->IsDataAvailable(CF_HDROP) || m_bDragOver) {
return de;
}
/*
if ((dwKeyState & (MK_CONTROL|MK_SHIFT) ) == (MK_CONTROL|MK_SHIFT)) {
// Ctrl和Shift同时按下表示对象以链接方式进入
de = DROPEFFECT_LINK;
} else if ((dwKeyState & MK_CONTROL) == MK_CONTROL) {
// Ctrl按下表示对象以复制方式进入
de = DROPEFFECT_COPY;
} else if ((dwKeyState & MK_ALT) == MK_ALT) {
// Alt 按下表示对象以移动方式进入
de = DROPEFFECT_MOVE;
} else {
//缺省为以移动方式进入
de = DROPEFFECT_MOVE;
}
*/
return DROPEFFECT_COPY;
}DROPEFFECT CTestWnd::OnDragOver(COleDataObject* pDataObject) {
return m_bDragOver?DROPEFFECT_COPY:DROPEFFECT_NONE;
}void CTestWnd::OnDragLeave() {
if(m_bDragOver) m_bDragOver = FALSE;
}BOOL CTestWnd::OnDrop(COleDataObject* pDataObject) {
OnDragLeave();FORMATETC fmtetc = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM stgmed;
TCHAR szFileName[_MAX_PATH + 1];
if(!pDataObject->GetData(CF_HDROP, &stgmed, &fmtetc)) {
return TRUE;
}HDROP hdrop = (HDROP)GlobalLock(stgmed.hGlobal);
if(NULL == hdrop) {
ReleaseStgMedium(&stgmed);
return TRUE;
}BOOL bAddedFiles = FALSE;
UINT nFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
for(UINT nNames = 0; nNames < nFiles; nNames++) {
ZeroMemory(szFileName, _MAX_PATH + 1);
DragQueryFile(hdrop, nNames, (LPTSTR)szFileName, _MAX_PATH + 1);
MessageBox(szFileName);
}
GlobalUnlock(hdrop);
ReleaseStgMedium(&stgmed);return TRUE;
}