📄 ondragenter(),ondragmove(),ondrop().txt
字号:
本例的窗口对象是从CView派生的,CView类实现了OnDragEnter(),OnDragMove(),OnDrop()等成员函数,实现拖拽操作在派生类中重载这些函数就可以了。但是对于一般的窗口CWnd是没有这些虚成员函数的。一个更加一般的做法是从COleDropTarget派生一个类,重载COleDropTarget的成员函数OnDragEnter(),OnDragMove(),OnDrop(),然后在窗口类中定义一个COleDropTarget派生类的对象,并把窗口注册给这个对象就可以实现同样的拖拽效果了。
剪贴板技术是由WINDOWS操作系统提供的用于进程内或进程间通信的一种机制。不光是拷贝粘贴操作需要用到剪贴板,拖拽也离不开它,并且他们将数据放到剪贴板上的一些代码也极其相似。本例将展示给大家怎样使用剪贴板。
剪贴板特点是:协议简单,数据传输由用户驱动。在很多不是由用户驱动但要做很多数据通信的应用程序可以采用其他的进程间通信的方法。进程间通信详细信息请您参见MSDN中Platform SDK的Interprocess Communication中的相应文档。
WINDOWS系统缺省支持的8种剪贴板格式:
CF_BITMAP CF_DIB
CF_DIB
CF_ENHMETAFILE
CF_METAFILEPICT
CF_OEMTEXT
CF_TEXT
CF_UNICODETEXT
//将数据放到剪贴板上,一般要5个步骤,如下所示:
void CClipExamView::OnEditCopy()
{
// 1 创建一个新的COleDataSource对象
COleDataSource* pSource = new COleDataSource();
// 2 创建一个新的CSharedFile对象
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
CString text = _T("Testing 1... 2... 3...");
// 3 将信息写到CSharedFile对象中
sf.Write(text, text.GetLength());
HGLOBAL hMem = sf.Detach();
if (!hMem) return;
// 4 将CSharedFile中的数据内存传递到CacheGlobalData()中
pSource->CacheGlobalData(CF_TEXT, hMem);
// 5 用SetClipboard()将数据放置到剪贴板上
pSource->SetClipboard();
}
//除了采用上面的方法,还可以采用序列化的方式将数据写到剪贴板上。
//采用序列化的方式将数据写到剪贴板是很有用的,这样可以给自己的应用程序定义一个自定义的剪贴板。
//这可以通过注册一个自定义的剪贴板格式来实现,并且将数据序列化到CSharedFile对象中
//下面举例说明如何将一个CObject对象序列化到剪贴板中
void SerializeToClipboard(CObject* obj,CString FormatName)
{
COleDataSource* pSource = new COleDataSource();
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
UINT format=RegisterClipboardFormat(FormatName);
CArchive ar(&sf,CArchive::store);
obj->Serialize(ar);
ar.Close();
HGLOBAL hMem = sf.Detach();
if (!hMem) return;
pSource->CacheGlobalData(format, hMem);
pSource->SetClipboard();
}
//从剪贴板上读取数据,正好与OnEditCopy相反,也是5个步骤
void CClipExamView::OnEditPaste()
{
// 1 创建一个新的COleDataObject对象
COleDataObject obj;
// 2 检查剪贴板中数据格式是否满足需要
if (obj.AttachClipboard())
{
if (obj.IsDataAvailable(CF_TEXT))
{
HGLOBAL hmem = obj.GetGlobalData(CF_TEXT);
CMemFile sf((BYTE*) ::GlobalLock(hmem), ::GlobalSize(hmem));
CString buffer;
// 3 将剪贴板中的数据放到CMemFile对象中
//当然你可以用其他的内存对象或者自定义的数据类型或对象
LPSTR str = buffer.GetBufferSetLength(::GlobalSize(hmem));
// 4 从CMemFile对象中读出数据
sf.Read(str, ::GlobalSize(hmem));
::GlobalUnlock(hmem);
// 5 用数据做相关操作
TRACE("Paste received = '%s'\r\n", buffer);
}
}
}
//同样你可以采用序列化的方式将数据从剪贴板中取出
void SerializeFromClipboard(COleDataObject* obj,CObject* cobj,CString FormatName)
{
//根据剪贴板格式的名称取得该格式的唯一标识
//详细信息参考API函数:RegisterClipboardFormat
UINT format=RegisterClipboardFormat(FormatName);
//判断剪贴板数据格式
if(obj->IsDataAvailable(format))
{
//读取剪贴板中的数据
HGLOBAL hmem=obj->GetGlobalData(format);
//创建内存文件
CMemFile sf((BYTE*)::GlobalLock(hmem),::GlobalSize(hmem));
//序列化操作
CArchive ar(sf,CArchive::Load);
cobj->Serialize(ar);
//后续清理
ar.Close();
::GlobalUnlock(hmem);
}
}
//只要对上面的代码做少量的改动就可以实现拖拽支持。
//当一个被拖拽的对象进入一个窗口的时候,由该窗口决定是否接受该对象
//在一个拖拽操作过程中,当鼠标在一个窗口上移动的时候OnDragOver被调用
//第一个参数pDataObject表示被拖动的数据对象
//第二个参数dwKeyState表示键盘的状态
//第三个参数point表示当前的鼠标的位置
//返回值用于描述是否接受拖拽,DROPEFFECT_NONE表示不接受,否则接受
DROPEFFECT CClipExamView::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
if (pDataObject->IsDataAvailable(CF_TEXT))
return DROPEFFECT_COPY; //接受拖拽
else
return DROPEFFECT_NONE; //不接受拖拽
}
//当用户释放鼠标时,就可以来实现真正的数据接受了
//第一个参数pDataObject表示被拖动的数据对象
//第二个参数dropEffect表示操作类型
//第三个参数point表示当前的鼠标的位置
BOOL CClipExamView::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
//判断剪贴板格式是否满足要求
if (pDataObject->IsDataAvailable(CF_TEXT))
{
//从数据对象中取出数据
HGLOBAL hmem = pDataObject->GetGlobalData(CF_TEXT);
CMemFile sf((BYTE*) ::GlobalLock(hmem), ::GlobalSize(hmem));
CString buffer;
LPSTR str = buffer.GetBufferSetLength(::GlobalSize(hmem));
sf.Read(str, ::GlobalSize(hmem));
::GlobalUnlock(hmem);
//用'buffer'中的数据做相应的处理工作
TRACE("OnDrop received = '%s'\r\n", buffer);
return TRUE;
}
return FALSE;
}
//给拖拽操作设置数据源,并且启动拖拽操作
void CClipExamView::OnLButtonDown(UINT nFlags, CPoint point)
{
//创建一个COleDataSource,可以认为它就是一个存放将被拖放的数据对象
COleDataSource* pSource = new COleDataSource();
//创建一个CSharedFile对象
CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
CString text = _T("Testing 1... 2... 3...");
//将数据写入CSharedFile对象
sf.Write(text, text.GetLength());
//取得数据对象的内存句柄,并将数据放入剪贴板
HGLOBAL hMem = sf.Detach();
if (!hMem) return;
//CacheGlobalData()的第一个参数描述放入剪贴板的数据的格式
//第2个参数描述放入剪贴板的数据的全局内存句柄
pSource->CacheGlobalData(CF_TEXT, hMem);
//启动拖拽操作,函数DoDragDrop到拖拽操作完成才返回
//从交互上看,就是等到用户释放鼠标左键才结束操作
pSource->DoDragDrop();
//在此可以做一些清理工作,因为在这里拖拽操作已经完成,
//没有用的内存可以释放了
}
//使一个View成为一个Drop Target
void CClipExamView::OnInitialUpdate()
{
CView::OnInitialUpdate();
//将窗口本身注册给一个COleDropTarget对象,一定要注意这是必不可少的
//如果你不这样做,那么视图窗口将不会接受拖拽操作中的对象
//也就是说这个窗口不会成为拖拽中的对象的目标窗口(Drop Target)
m_DropTarget.Register(this);
//m_DropTarget在clipexamView.h中已经声明为COleDropTarget m_DropTarget;
}
最后值得注意两点:
1 要确保在应用类的InitInstance()中调用AfxOleInit()
在应用程序的入口处(一般是在应用类的InitInstance()成员函数中)调用AfxOleInit()
这是用来初始化OLE库的
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
2 确保stdafx.h中有相关包含语句,具体请您参考工程
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -