對(duì)象拖放是指對(duì)某一指定的對(duì)象,利用鼠標(biāo)拖動(dòng)的方法,在不同應(yīng)用的窗口之間、同一應(yīng)用的不同窗口之間或同一應(yīng)用的同一窗口內(nèi)進(jìn)行移動(dòng)、復(fù)制(粘貼)等操作的技術(shù)。
利用對(duì)象拖放,可以為用戶提供方便、直觀的操作界面。
實(shí)現(xiàn)對(duì)象拖放技術(shù),需要了解、使用MFC的CView、COleDataSource和COleDropTarget等類,并利用這些類協(xié)同工作。
本文討論了對(duì)象拖放技術(shù),并研究了如何利用MFC實(shí)現(xiàn)該技術(shù)。
利用MFC實(shí)現(xiàn)對(duì)象拖放,編程比較容易,代碼可讀性好。
修改稿
利用MFC實(shí)現(xiàn)對(duì)象拖放
本文討論了對(duì)象拖放技術(shù),并研究了如何利用MFC實(shí)現(xiàn)該技術(shù);利用MFC實(shí)現(xiàn)對(duì)象拖放,編程比較容
易,代碼可讀性好。
1.對(duì)象拖放概念
對(duì)象拖放是指對(duì)某一指定的對(duì)象,利用鼠標(biāo)拖動(dòng)的方法,在不同應(yīng)用的窗口之間、同一應(yīng)用的不同窗口之間或同一應(yīng)用的同一窗口內(nèi)進(jìn)行移動(dòng)、復(fù)制(粘貼)等操作的技術(shù)?! ?
對(duì)象拖放是在操作系統(tǒng)的幫助下完成的。 要開始一次拖動(dòng), 首先需要指定或生成被拖動(dòng)的對(duì)象,然后指定整個(gè)拖放操作過程所使用的數(shù)據(jù)格式,并按指定的數(shù)據(jù)格式提供數(shù)據(jù),最后啟動(dòng)對(duì)象拖放操作;當(dāng)對(duì)象在某一窗口內(nèi)落下時(shí),拖放過程結(jié)束,接收拖放對(duì)象的窗口按指定的數(shù)據(jù)格式提取有關(guān)數(shù)據(jù),并根據(jù)提取的數(shù)據(jù)生成對(duì)象。
2.MFC中用于對(duì)象拖放的類
MFC(Microsoft Foundation ClassLibrary)為實(shí)現(xiàn)對(duì)象拖放提供了如下三個(gè)類。為便于后邊的討論我們先來熟悉一下這些類?!?
2.1.COleDataSource。用于啟動(dòng)一次拖放操作,并向系統(tǒng)提供拖放對(duì)象的數(shù)據(jù)。類中的成員函數(shù)有如下三種:
a.設(shè)定提供數(shù)據(jù)的方式和使用的數(shù)據(jù)格式。提供數(shù)據(jù)的方式有兩種,一種是即時(shí)方式,另一種是延遲方式;即時(shí)方式需要在拖動(dòng)開始之前提供數(shù)據(jù);延遲方式不需要立即提供數(shù)據(jù),當(dāng)系
統(tǒng)請(qǐng)求有關(guān)數(shù)據(jù)時(shí),由OnRenderData()等虛函數(shù)提供所需的數(shù)據(jù)?! ?
可以用CacheGlobalData()等函數(shù)指定使用即時(shí)方式提供數(shù)據(jù),也可以用DelayRenderData
()等函數(shù)指定使用延時(shí)方式提供數(shù)據(jù)?! ?
b.響應(yīng)請(qǐng)求,提供數(shù)據(jù)。應(yīng)當(dāng)重載OnRenderFileData()或其他相應(yīng)的虛函數(shù),以提供有關(guān)數(shù)據(jù)(后邊將詳細(xì)討論)。
c.實(shí)施拖放操作。調(diào)用函數(shù)DoDragDrop(),開始實(shí)施拖放操作。
2.2.OleDataTarget。用于準(zhǔn)備接收拖放對(duì)象的目標(biāo)窗口;一個(gè)窗口要想能夠接收拖放對(duì)象,必須包含一個(gè)COleDataTarget對(duì)象,并注冊(cè)該對(duì)象。類中主要成員函數(shù):
a.注冊(cè)。函數(shù)Register()注冊(cè)該對(duì)象,以便使窗口能夠接收拖放對(duì)象。
b.響應(yīng)拖放過程中的動(dòng)作(虛成員函數(shù)) 當(dāng)鼠標(biāo)首次進(jìn)入窗口時(shí)系統(tǒng)將調(diào)用OnDragEnter(),當(dāng)鼠標(biāo)移出窗口時(shí)系統(tǒng)將調(diào)用OnDragLeave(), 當(dāng)鼠標(biāo)在窗口內(nèi)移動(dòng),系統(tǒng)將重復(fù)調(diào)用調(diào)用OnDragOver(),當(dāng)對(duì)象在窗口內(nèi)落下調(diào)用OnDrop()。
2.3.OleDataObject.用于接收拖放對(duì)象,類中主要成員函數(shù)有兩種:
a.確定可以使用的數(shù)據(jù)格式。IsDataAvailable()等函數(shù)確定指定數(shù)據(jù)格式是否可用;
b.獲取數(shù)據(jù)。GetData()、GetFileData()等函數(shù)用于按指定數(shù)據(jù)格式獲得數(shù)據(jù)。
3.利用MFC實(shí)現(xiàn)對(duì)象拖放
要實(shí)現(xiàn)一次對(duì)象拖放,需要做三方面的工作:對(duì)象所在的窗口準(zhǔn)備拖放對(duì)象并啟拖動(dòng)操作,接受對(duì)象的窗口響應(yīng)有關(guān)拖放消息并接受落下的對(duì)象,以及拖放完成時(shí)的后期處理。以下分別予以介紹?! ?
3.1. 拖動(dòng)操作的啟動(dòng)。拖放操作一般是從單擊鼠標(biāo)左鍵開始。在消息WM_LBUTTONDOWN的響應(yīng)
函數(shù)OnLButtonDown(...)中,首先要判定是否選定了某一對(duì)象,如果未選定或選定多個(gè),則不能進(jìn)
行拖放操作;如果選定了一個(gè)對(duì)象,則可以進(jìn)行拖放操作?! ?
要啟動(dòng)一次拖放操作,需要先準(zhǔn)備一個(gè)COleDataSource對(duì)象。注意到類COleClientIten和類
COleServerItem都是從類COleDataSource上派生的,如果選定的是COleClientItem對(duì)象或者是
COleServerItem對(duì)象,則可以直接使用;否則,需要生成一個(gè)COleDataSource對(duì)象,值得注意的
是:需要象上文中所說的,應(yīng)該指定使用的數(shù)據(jù)格式,并按指定格式提供對(duì)象的有關(guān)數(shù)據(jù)。
下面給出準(zhǔn)備數(shù)據(jù)源的例子:
class myDataSource: public COleDataSource
{
public:
COLORREF color;
CString str;
protected:
virtual BOOL OnRenderFileData(LPFORMATETC,CFile*);
//......
};
BOOL myDataSource::OnRenderFileData(LPFORMATETC lpFormatEtc,CFile* pFile)
{
if(lpFormatEtc->cfFormat==CF_TEXT)
{
pFile.Write("Test DragDrop",13); //Magic String
pFile.Write(&color,sizeof(COLORREF));
int len= str.GetLength();
pFile.Write(len,sizeof(int));
pFile.Write(str,len);
return TRUE;
}
COleDataSource::OnRenderFileData(lpFormatEtc,pFile);
return FALSE;
}
有了以上數(shù)據(jù)源之后,就可以在消息WM_LBUTTON的響應(yīng)函數(shù)OnLButtonDown()中,按如下方式,指定使用的數(shù)據(jù)格式:
myDataSource* pItemDragDrop=new myDataSource;
pItemDragDrop->str="This string will dragdrop to another place";
pItemDragDrop->DelayRenderFileData(CF_TEXT,NULL);
指定好使用的數(shù)據(jù)格式之后,調(diào)用此對(duì)象的成員函數(shù)DoDragDrop(...),啟動(dòng)對(duì)象拖放操作。需要注意的是,函數(shù)DoDragDrop(...)并不立即返回,而是要等到鼠標(biāo)按鈕彈起之后。
3.2. 拖放對(duì)象的接收。缺省情況下,一般的窗口是不能接收拖放對(duì)象的;要使窗口可以接收拖放對(duì)象,需要在窗口類定義中加入成員對(duì)象COleDropTarget,并在生成窗口時(shí)調(diào)用函數(shù)COleDataTarget::Register()。例如:
Class myView : public CScrollView
{
private:
COleDropTarget oleTarget;
protected:
virtual int OnCreate(LPCREATESTRUCT);
//......
}
int myView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//......
dropTarget.Register(this);
return 0;
}
為實(shí)現(xiàn)拖放對(duì)象的接收,還應(yīng)重載CView或COleDropTarget的虛函數(shù):COnDragMove()、OnDragEnter()和OnDrop()等。函數(shù)OnDragEnter()、OnDragMove()應(yīng)根據(jù)鼠標(biāo)在窗口中的位置,返回以下數(shù)值:
DROPEFFECT_MOVE---表明可以把對(duì)象復(fù)制到現(xiàn)在的窗口、現(xiàn)在的位置;
DROPEFFECT_COPY---表明可以把對(duì)象從原來的窗口、原來的位置移到現(xiàn)在的窗口、現(xiàn)在的位置;
DROPEFFECT_NONE---表明不能在該窗口的該位置放下。
下例只允許移動(dòng)對(duì)象,而不允許復(fù)制對(duì)象:
DROPEFFECT myView::OnDragEnter(......)
{
return DROPEFFECT_MOVE;
}
DROPEFFECT myView::OnDragOver(......)
{
return DROPEFFECT_MOVE;
}
函數(shù)OnDrop()應(yīng)處理拖動(dòng)對(duì)象放下后的工作。該函數(shù)的參數(shù)pDataObjec指向一個(gè)COleDataObject對(duì)象,利用指針,可以獲取有關(guān)數(shù)據(jù)。該函數(shù)的一般實(shí)現(xiàn)是:
a.檢查對(duì)象的數(shù)據(jù)格式: 利用函數(shù)COleDataObject::IsDataAvailable();
b.按指定的格式獲取數(shù)據(jù):利用COleDataObject::GetFileData()等函數(shù);
c.建立對(duì)象(可能與原對(duì)象相同,也可能不建立對(duì)象僅使用對(duì)象中的數(shù)據(jù)):利用以上步驟
得到的數(shù)據(jù)建立對(duì)象。例如:
char magic_string[13];
COLORREF color;
CString str;
int len;
myDataSource* pMyData;
if(IsDataAvailable(CF_TEXT))
{
CFile file=GetFileData(CF_TEXT);
file.Read(magic_string,13);
if(strncmp(magic_string,"Test DragDrop",13)==0)
{
file.Read(&color,sizeof(COLORREF));
file.Read(len,sizeof(int));
file.Read(str,len);
CClientDC dc(this);
dc.SetTextColor(color);
dc.SetBkMode(TRANSPARENT);
dc.TextOut(100,50,str,len);
pMyData=new myDataSource;
pMyData->color=color;
pMyData->str=str;
}
}
對(duì)于COleClientItem或COleServerItem對(duì)象,可以按以下方法很容易地重建對(duì)象:
COleClient* pItem=GetDocument()->CreateNewItem();
pItem->CreateFrom(pDataObject);
3.3. 拖放操作的結(jié)束函數(shù)DoDragDrop()返回時(shí),拖放過程結(jié)束。函數(shù)DoDragDrop()的返回值,表明了對(duì)象的拖放結(jié)果。
DROPEFFECT_MOVE:對(duì)象被放到他處,需刪除原對(duì)象
DROPEFFECT_COPY:對(duì)象被復(fù)制到他處,不刪除原對(duì)象
DROPEFFECT_NONE:未能實(shí)現(xiàn)拖放,無需刪除原對(duì)象
例如:
int DragEffect=pItemTracking->DoDragDrop(......);
switch(DragEffect)
{
case DROPEFFECT_MOVE:
delete pItemTracking;
GetDocument()->UpdateAllItems(NULL);
GetDocument()->UpdateAllViews(NULL);
break;
case DROPEFFECT_COPY:
case DROPEFFECT_NONE:
default:
break;
}