上一次我们实现了让窗口支持DropTarget,我们实现了一个叫做IDropTarget的接口。但是还没有给控件实现,当然如果你在这个窗口里面只有一个控件实现DropTarget的话,你完全可以使用Rect来进行区域限制,但是这样还是比较麻烦,还要手动添加一堆代码。所以我们直接让控件实现这个功能,只需要一个bool属性,就可以控制一个控件是否能接受拖拽。
为了减少源代码的修改,我们弄下面这样一个接口,让CPaintManagerUI去实现。
class IDuiDropTarget
{
public:
virtual HRESULT OnDragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) = 0;
virtual HRESULT OnDragOver(DWORD grfKeyState, POINTL pt,DWORD *pdwEffect) = 0;
virtual HRESULT OnDragLeave() = 0;
virtual HRESULT OnDrop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) = 0;
};
其实我们完全可以让CPaintManagerUI直接去继承IDropTarget,但是这样做代码会比较乱,CPaintManagerUI里面的代码会增添很多,所以我们还是把多余的代码扔到我们自己实现的CDropTargetEx里面去吧。
我们在CDropTargetEx里面增加一个IDuiDropTarget* m_pDuiDropTarget;成员,用来操作CPaintManagerUI的对象。在调用bool DragDropRegister(IDuiDropTarget* pDuiDropTarget,HWND hWnd,DWORD AcceptKeyState=MK_LBUTTON);函数时,把CPaintManagerUI的指针传入。然后在CDropTargetEx的实现里面去回调IDuiDropTarget的函数。
class CDropTargetEx : public IDropTarget
{
public:
CDropTargetEx(void);
bool DragDropRegister(IDuiDropTarget* pDuiDropTarget,HWND hWnd,DWORD AcceptKeyState=MK_LBUTTON);
bool DragDropRevoke(HWND hWnd);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, __RPC__deref_out void **ppvObject);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
//进入
HRESULT STDMETHODCALLTYPE DragEnter(__RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect);
//移动
HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect);
//离开
HRESULT STDMETHODCALLTYPE DragLeave();
//释放
HRESULT STDMETHODCALLTYPE Drop(__RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect);
private:
~CDropTargetEx(void);
HWND m_hWnd;
IDropTargetHelper* m_piDropHelper;
bool m_bUseDnDHelper;
IDuiDropTarget* m_pDuiDropTarget;
DWORD m_dAcceptKeyState;
ULONG m_lRefCount;
};
///
CDropTargetEx::CDropTargetEx(void)
{
m_lRefCount = 1;
// 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(void)
{
if (m_piDropHelper)
{
m_piDropHelper->Release();
}
m_bUseDnDHelper = false;
m_lRefCount = 0;
}
bool CDropTargetEx::DragDropRegister(IDuiDropTarget* pDuiDropTarget,HWND hWnd,DWORD AcceptKeyState)
{
if(!IsWindow(hWnd))return false;
m_pDuiDropTarget = pDuiDropTarget;
HRESULT s = ::RegisterDragDrop (hWnd,this);
m_hWnd = hWnd;
if(SUCCEEDED(s))
{
m_dAcceptKeyState = AcceptKeyState;
return true;
}
else { return false; }
}
bool CDropTargetEx::DragDropRevoke(HWND hWnd)
{
if(!IsWindow(hWnd))return false;
HRESULT s = ::RevokeDragDrop(hWnd);
return SUCCEEDED(s);
}
HRESULT STDMETHODCALLTYPE CDropTargetEx::QueryInterface(REFIID riid, __RPC__deref_out void **ppvObject)
{
static const QITAB qit[] =
{
QITABENT(CDropTargetEx, IDropTarget),
{ 0 }
};
return QISearch(this, qit, riid, ppvObject);
}
ULONG STDMETHODCALLTYPE CDropTargetEx::AddRef()
{
return InterlockedIncrement(&m_lRefCount);
}
ULONG STDMETHODCALLTYPE CDropTargetEx::Release()
{
ULONG lRef = InterlockedDecrement(&m_lRefCount);
if (0 == lRef)
{
delete this;
}
return m_lRefCount;
}
//进入
HRESULT STDMETHODCALLTYPE CDropTargetEx::DragEnter(__RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
{
if ( m_bUseDnDHelper )
{
m_piDropHelper->DragEnter (m_hWnd, pDataObj, (LPPOINT)&pt, *pdwEffect );
}
return m_pDuiDropTarget->OnDragEnter(pDataObj,grfKeyState,pt,pdwEffect);
}
//移动
HRESULT STDMETHODCALLTYPE CDropTargetEx::DragOver(DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
{
if ( m_bUseDnDHelper )
{
m_piDropHelper->DragOver((LPPOINT)&pt, *pdwEffect);
}
return m_pDuiDropTarget->OnDragOver(grfKeyState,pt,pdwEffect);
}
//离开
HRESULT STDMETHODCALLTYPE CDropTargetEx::DragLeave()
{
if ( m_bUseDnDHelper )
{
m_piDropHelper->DragLeave();
}
return m_pDuiDropTarget->OnDragLeave();
}
//释放
HRESULT STDMETHODCALLTYPE CDropTargetEx::Drop(__RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, __RPC__inout DWORD *pdwEffect)
{
m_piDropHelper->Drop ( pDataObj, (LPPOINT)&pt, *pdwEffect );
return m_pDuiDropTarget->OnDrop(pDataObj,grfKeyState,pt,pdwEffect);
}
这样就相当于CPaintManagerUI间接地实现了IDropTarget接口。我们下一步的任务就是,把CPaintManagerUI捕获到的拖拽事件“路由”给CControlUI。
为了让CControlUI支持这个事件,我们先给它添加下面几个成员函数。
//拖拽相关
virtual void OnDragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINT pt, DWORD *pdwEffect);
virtual void OnDragOver(DWORD grfKeyState, POINT pt,DWORD *pdwEffect);
virtual void OnDragLeave();
virtual void OnDrop(IDataObject *pDataObj, DWORD grfKeyState, POINT pt, DWORD *pdwEffect);
注意,这里面是虚函数,这样写更灵活,因为CControlUI的派生类很多,方便以后重写。
同样地,我们在给CControlUI加一个bool m_bDragEnabled;属性,并在SetAttribute里面加入如下的代码
else if( _tcscmp(pstrName, _T("droptarget")) == 0 ) SetDropEnabled(_tcscmp(pstrValue, _T("true")) == 0);
以便在xml里面开启和禁止这个功能。
如何让控件里面新加入的这几个函数响应,就需要在CPaintManagerUI里面做手脚了。这一点也是DUI技术的核心,把窗口的事件过滤到控件,在我们操作控件时仿佛像直接操作一个窗口一样。这一点我们可以效仿一下,ButtonDown ,ButtonUp和MouseMove的处理。
1.首先,做第一个模仿,给CPaintManagerUI加一个CControlUI* m_pEventDrop;变量,这个表示当前接收DropTarget的控件,
2.在HRESULT OnDragOver(DWORD grfKeyState, POINTL pt,DWORD *pdwEffect);事件并没有传递IDataObject *pDataObj,在CControlUI响应virtual void OnDragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINT pt, DWORD *pdwEffect);时,恰好是OnDragOver路由过来的。所以我们要保存这个IDataObject *pDataObj对象,所以在CPaintManagerUI里面还要加一个 IDataObject* m_pCurDataObject;变量。
3.下面是关键,我们看一个事件是如何从窗口过滤到 控件
HRESULT CPaintManagerUI::OnDragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
m_pCurDataObject = pDataObj;
POINT pt={ptl.x,ptl.y};
::ScreenToClient(m_hWndPaint,&pt);
CControlUI* pHover = FindControl(pt);
if( pHover == NULL )
{
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
// Generate mouse hover event
pHover->OnDragEnter(pDataObj,grfKeyState,pt,pdwEffect);
m_pEventDrop = pHover;
return S_OK;
}
HRESULT CPaintManagerUI::OnDragOver(DWORD grfKeyState, POINTL ptl,DWORD *pdwEffect)
{
POINT pt={ptl.x,ptl.y};
::ScreenToClient(m_hWndPaint,&pt);
m_ptLastMousePos = pt;
CControlUI* pNewHover = FindControl(pt);
if(pNewHover==NULL)
{
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
if( pNewHover != NULL && pNewHover->GetManager() != this )
{
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
if( pNewHover != m_pEventDrop && m_pEventDrop != NULL ) {
m_pEventDrop->OnDragLeave();
m_pEventDrop = NULL;
}
if( pNewHover != m_pEventDrop && pNewHover != NULL ) {
pNewHover->OnDragEnter(m_pCurDataObject,grfKeyState,pt,pdwEffect);
m_pEventDrop = pNewHover;
}
if( pNewHover != NULL ) {
pNewHover->OnDragOver(grfKeyState,pt,pdwEffect);
}
return S_OK;
}
HRESULT CPaintManagerUI::OnDragLeave()
{
m_pCurDataObject = NULL;
if( m_pEventDrop != NULL ) {
m_pEventDrop->OnDragLeave();
m_pEventDrop = NULL;
}
return S_OK;
}
HRESULT CPaintManagerUI::OnDrop(__RPC__in_opt IDataObject *pDataObj, DWORD grfKeyState, POINTL ptl, __RPC__inout DWORD *pdwEffect)
{
POINT pt={ptl.x,ptl.y};
::ScreenToClient(m_hWndPaint,&pt);
if( m_pEventDrop != NULL ) {
m_pEventDrop->OnDrop(pDataObj,grfKeyState,pt,pdwEffect);
}else
{
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
return S_OK;
}
这些代码,是我照搬的ButtonDown ,ButtonUp和MouseMove的处理。大家可以在MessageHanler里面看一下,因为这个事件和鼠标的事件太相似了。
最后,在控件响应时,如果控件没有开启droptarget属性的话,就让父控件去响应,这一点也是模仿的鼠标事件,如果当前控件mouse属性为false的话,则去调用父控件的mouse消息。
void CControlUI::OnDragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINT pt, DWORD *pdwEffect)
{
if (IsDropEnabled())
{
*pdwEffect = DROPEFFECT_COPY;
}else
{
*pdwEffect = DROPEFFECT_NONE;
if( m_pParent != NULL )m_pParent->OnDragEnter(pDataObj,grfKeyState,pt,pdwEffect);
}
}
void CControlUI::OnDragOver(DWORD grfKeyState, POINT pt,DWORD *pdwEffect)
{
if (IsDropEnabled())
{
*pdwEffect = DROPEFFECT_COPY;
}else
{
*pdwEffect = DROPEFFECT_NONE;
if( m_pParent != NULL )m_pParent->OnDragOver(grfKeyState,pt,pdwEffect);
}
}
void CControlUI::OnDragLeave()
{
if (IsDropEnabled())
else
if( m_pParent != NULL )m_pParent->OnDragLeave();
}
void CControlUI::OnDrop(IDataObject *pDataObj, DWORD grfKeyState, POINT pt, DWORD *pdwEffect)
{
if (IsDropEnabled())
{
*pdwEffect = DROPEFFECT_COPY;
}else
{
*pdwEffect = DROPEFFECT_NONE;
if( m_pParent != NULL )m_pParent->OnDrop(pDataObj,grfKeyState,pt,pdwEffect);
}
}
到这里源码修改基本就完成了,我们现在可以测试一下,给一个控件指定droptarget = “true”属性。
这里我们看到了,在指定droptarget = “true”的控件上出现了,可拖放的图标。
在没有指定这个属性的控件上则是禁止状态
今天先介绍到这里了,有问题的话QQ联系我(Skilla:848861075)