對象拖放是指對某一指定的對象,利用鼠標(biāo)拖動的方法,在不同應(yīng)用的窗口之間、同一應(yīng)用的不同窗口之間或同一應(yīng)用的同一窗口內(nèi)進行移動、復(fù)制(粘貼)等操作的技術(shù)。
利用對象拖放,可以為用戶提供方便、直觀的操作界面。
實現(xiàn)對象拖放技術(shù),需要了解、使用MFC的CView、COleDataSource和COleDropTarget等類,并利用這些類協(xié)同工作。
本文討論了對象拖放技術(shù),并研究了如何利用MFC實現(xiàn)該技術(shù)。
利用MFC實現(xiàn)對象拖放,編程比較容易,代碼可讀性好。
修改稿
利用MFC實現(xiàn)對象拖放
本文討論了對象拖放技術(shù),并研究了如何利用MFC實現(xiàn)該技術(shù);利用MFC實現(xiàn)對象拖放,編程比較容
易,代碼可讀性好。
1.對象拖放概念
對象拖放是指對某一指定的對象,利用鼠標(biāo)拖動的方法,在不同應(yīng)用的窗口之間、同一應(yīng)用的不同窗口之間或同一應(yīng)用的同一窗口內(nèi)進行移動、復(fù)制(粘貼)等操作的技術(shù)?! ?
對象拖放是在操作系統(tǒng)的幫助下完成的。 要開始一次拖動, 首先需要指定或生成被拖動的對象,然后指定整個拖放操作過程所使用的數(shù)據(jù)格式,并按指定的數(shù)據(jù)格式提供數(shù)據(jù),最后啟動對象拖放操作;當(dāng)對象在某一窗口內(nèi)落下時,拖放過程結(jié)束,接收拖放對象的窗口按指定的數(shù)據(jù)格式提取有關(guān)數(shù)據(jù),并根據(jù)提取的數(shù)據(jù)生成對象。
2.MFC中用于對象拖放的類
MFC(Microsoft Foundation ClassLibrary)為實現(xiàn)對象拖放提供了如下三個類。為便于后邊的討論我們先來熟悉一下這些類?!?
2.1.COleDataSource。用于啟動一次拖放操作,并向系統(tǒng)提供拖放對象的數(shù)據(jù)。類中的成員函數(shù)有如下三種:
a.設(shè)定提供數(shù)據(jù)的方式和使用的數(shù)據(jù)格式。提供數(shù)據(jù)的方式有兩種,一種是即時方式,另一種是延遲方式;即時方式需要在拖動開始之前提供數(shù)據(jù);延遲方式不需要立即提供數(shù)據(jù),當(dāng)系
統(tǒng)請求有關(guān)數(shù)據(jù)時,由OnRenderData()等虛函數(shù)提供所需的數(shù)據(jù)?! ?
可以用CacheGlobalData()等函數(shù)指定使用即時方式提供數(shù)據(jù),也可以用DelayRenderData
()等函數(shù)指定使用延時方式提供數(shù)據(jù)。
b.響應(yīng)請求,提供數(shù)據(jù)。應(yīng)當(dāng)重載OnRenderFileData()或其他相應(yīng)的虛函數(shù),以提供有關(guān)數(shù)據(jù)(后邊將詳細(xì)討論)?!?
c.實施拖放操作。調(diào)用函數(shù)DoDragDrop(),開始實施拖放操作。
2.2.OleDataTarget。用于準(zhǔn)備接收拖放對象的目標(biāo)窗口;一個窗口要想能夠接收拖放對象,必須包含一個COleDataTarget對象,并注冊該對象。類中主要成員函數(shù):
a.注冊。函數(shù)Register()注冊該對象,以便使窗口能夠接收拖放對象。
b.響應(yīng)拖放過程中的動作(虛成員函數(shù)) 當(dāng)鼠標(biāo)首次進入窗口時系統(tǒng)將調(diào)用OnDragEnter(),當(dāng)鼠標(biāo)移出窗口時系統(tǒng)將調(diào)用OnDragLeave(), 當(dāng)鼠標(biāo)在窗口內(nèi)移動,系統(tǒng)將重復(fù)調(diào)用調(diào)用OnDragOver(),當(dāng)對象在窗口內(nèi)落下調(diào)用OnDrop()。
2.3.OleDataObject.用于接收拖放對象,類中主要成員函數(shù)有兩種:
a.確定可以使用的數(shù)據(jù)格式。IsDataAvailable()等函數(shù)確定指定數(shù)據(jù)格式是否可用;
b.獲取數(shù)據(jù)。GetData()、GetFileData()等函數(shù)用于按指定數(shù)據(jù)格式獲得數(shù)據(jù)?! ?
3.利用MFC實現(xiàn)對象拖放
要實現(xiàn)一次對象拖放,需要做三方面的工作:對象所在的窗口準(zhǔn)備拖放對象并啟拖動操作,接受對象的窗口響應(yīng)有關(guān)拖放消息并接受落下的對象,以及拖放完成時的后期處理。以下分別予以介紹。
3.1. 拖動操作的啟動。拖放操作一般是從單擊鼠標(biāo)左鍵開始。在消息WM_LBUTTONDOWN的響應(yīng)
函數(shù)OnLButtonDown(...)中,首先要判定是否選定了某一對象,如果未選定或選定多個,則不能進
行拖放操作;如果選定了一個對象,則可以進行拖放操作。
要啟動一次拖放操作,需要先準(zhǔn)備一個COleDataSource對象。注意到類COleClientIten和類
COleServerItem都是從類COleDataSource上派生的,如果選定的是COleClientItem對象或者是
COleServerItem對象,則可以直接使用;否則,需要生成一個COleDataSource對象,值得注意的
是:需要象上文中所說的,應(yīng)該指定使用的數(shù)據(jù)格式,并按指定格式提供對象的有關(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)用此對象的成員函數(shù)DoDragDrop(...),啟動對象拖放操作。需要注意的是,函數(shù)DoDragDrop(...)并不立即返回,而是要等到鼠標(biāo)按鈕彈起之后?! ?
3.2. 拖放對象的接收。缺省情況下,一般的窗口是不能接收拖放對象的;要使窗口可以接收拖放對象,需要在窗口類定義中加入成員對象COleDropTarget,并在生成窗口時調(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;
}
為實現(xiàn)拖放對象的接收,還應(yīng)重載CView或COleDropTarget的虛函數(shù):COnDragMove()、OnDragEnter()和OnDrop()等。函數(shù)OnDragEnter()、OnDragMove()應(yīng)根據(jù)鼠標(biāo)在窗口中的位置,返回以下數(shù)值:
DROPEFFECT_MOVE---表明可以把對象復(fù)制到現(xiàn)在的窗口、現(xiàn)在的位置;
DROPEFFECT_COPY---表明可以把對象從原來的窗口、原來的位置移到現(xiàn)在的窗口、現(xiàn)在的位置;
DROPEFFECT_NONE---表明不能在該窗口的該位置放下。
下例只允許移動對象,而不允許復(fù)制對象:
DROPEFFECT myView::OnDragEnter(......)
{
return DROPEFFECT_MOVE;
}
DROPEFFECT myView::OnDragOver(......)
{
return DROPEFFECT_MOVE;
}
函數(shù)OnDrop()應(yīng)處理拖動對象放下后的工作。該函數(shù)的參數(shù)pDataObjec指向一個COleDataObject對象,利用指針,可以獲取有關(guān)數(shù)據(jù)。該函數(shù)的一般實現(xiàn)是:
a.檢查對象的數(shù)據(jù)格式: 利用函數(shù)COleDataObject::IsDataAvailable();
b.按指定的格式獲取數(shù)據(jù):利用COleDataObject::GetFileData()等函數(shù);
c.建立對象(可能與原對象相同,也可能不建立對象僅使用對象中的數(shù)據(jù)):利用以上步驟
得到的數(shù)據(jù)建立對象。例如:
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;
}
}
對于COleClientItem或COleServerItem對象,可以按以下方法很容易地重建對象:
COleClient* pItem=GetDocument()->CreateNewItem();
pItem->CreateFrom(pDataObject);
3.3. 拖放操作的結(jié)束函數(shù)DoDragDrop()返回時,拖放過程結(jié)束。函數(shù)DoDragDrop()的返回值,表明了對象的拖放結(jié)果。
DROPEFFECT_MOVE:對象被放到他處,需刪除原對象
DROPEFFECT_COPY:對象被復(fù)制到他處,不刪除原對象
DROPEFFECT_NONE:未能實現(xiàn)拖放,無需刪除原對象
例如:
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;
}