关于Duilib的扩展——“拖放”实现(二)

上一次我们实现了让窗口支持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)






  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Skilla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值