树控件拖动.docx
《树控件拖动.docx》由会员分享,可在线阅读,更多相关《树控件拖动.docx(32页珍藏版)》请在冰豆网上搜索。
![树控件拖动.docx](https://file1.bdocx.com/fileroot1/2022-11/24/13092845-9ad6-48a2-aafc-438a28b7f4dc/13092845-9ad6-48a2-aafc-438a28b7f4dc1.gif)
树控件拖动
VC编程完美实现树型控件的拖动
2004-07-2017:
18作者:
一块三毛钱出处:
vckbase责任编辑:
方舟
1、基本拖动的实现
当我们要拖动一个项目时,树型视图控件会给它的父窗口发送TVN_BEGINDRAG通知消息。
可以在此处创建表示项目处在拖动操作中的图象,调用CreateDragImage函数产生一副缺省的图象,该函数创建的图象由条目图象和标签文本组成。
创建了拖动图象后,调用BeginDrag函数指定拖动图象的热点位置,然后调用DragEnter函数显示拖动图象。
接下来处理WM_MOUSEMOVE消息用于更新拖动图象,我们想让移动中的图象经过某些项目时高亮度显示,这可以调用SelectDropTarget来实现。
在调用SelectDropTarget前,我们先调用DragShowNolock(false)来隐藏图象列表,之后再调用DragShowNolock(true)来恢复图象列表的显示,这样就不会在拖动过程中留下难看的轨迹。
最后我们处理WM_LBUTTONUP消息用于完成拖动操作,在给消息中,我们需要完成结束拖动图想的显示、删除拖动图象、释放鼠标、节点的拷贝/删除等操作。
在节点的拷贝/删除操作中,如果是父节点拖到子节点上,我们可以先将父节点拷到根结点下的临时节点中,再从临时结点处拷到子节点,然后将根结点下的临时节点删除,这样做的目的是防止产生异常。
2、处理无意拖动
大家可能都有过这样的经历:
在鼠标按下时不小心移动了鼠标,这时系统就认为产生了一个移动操作。
如果我们不针对这种情况加以解决的话,就很容易产生误操作。
下面我们就提出一个解决方法:
设置时间延迟。
也就是说当用户按下鼠标后必须在原位置停留一段时间,才能激活拖动操作。
3、处理拖动过程中的滚动问题
当我们进行拖动时,如果目的节点不可见,则需要拖动滚动条或收拢其它一些节点以使得目的节点显示出来,无疑,这会给我们带来很大的不便。
下面我们就来给树型控件添加自动滚动支持。
设置一个定时器,在WM_TIMER消息中检测鼠标的位置,如果靠近树型控件的下边缘,则使得控件向下滚动。
靠近上边缘则向上滚动。
滚动速度根据鼠标的位置确定。
4、拖动过程中节点的智能展开
这一步我们要实现的功能是在拖动过程中当鼠标停留在某个节点上一段时间后,该节点会自动展开。
设置一个定时器,当鼠标在拖动过程中停止在某个节点上时,定时器被启动,再设置一变量保存当前的鼠标位置。
下面是实现的源代码
//XTreeCtrl.h
……
protected:
UINTm_TimerTicks;//处理滚动的定时器所经过的时间
UINTm_nScrollTimerID;//处理滚动的定时器
CPointm_HoverPoint;//鼠标位置
UINTm_nHoverTimerID;//鼠标敏感定时器
DWORDm_dwDragStart;//按下鼠标左键那一刻的时间
BOOLm_bDragging;//标识是否正在拖动过程中
CImageList*m_pDragImage;//拖动时显示的图象列表
HTREEITEMm_hItemDragS;//被拖动的标签
HTREEITEMm_hItemDragD;//接受拖动的标签
……
//XTreeCtrl.cpp
……
#defineDRAG_DELAY60
……
voidCXTreeCtrl:
:
OnBegindrag(NMHDR*pNMHDR,LRESULT*pResult)
{
NM_TREEVIEW*pNMTreeView=(NM_TREEVIEW*)pNMHDR;
*pResult=0;
//如果是无意拖动,则放弃操作
if((GetTickCount()-m_dwDragStart) return;
m_hItemDragS=pNMTreeView->itemNew.hItem;
m_hItemDragD=NULL;
//得到用于拖动时显示的图象列表
m_pDragImage=CreateDragImage(m_hItemDragS);
if(!
m_pDragImage)
return;
m_bDragging=true;
m_pDragImage->BeginDrag(0,CPoint(8,8));
CPointpt=pNMTreeView->ptDrag;
ClientToScreen(&pt);
m_pDragImage->DragEnter(this,pt);//"this"将拖动操作限制在该窗口
SetCapture();
m_nScrollTimerID=SetTimer(2,40,NULL);
}
voidCXTreeCtrl:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
HTREEITEMhItem;
UINTflags;
//检测鼠标敏感定时器是否存在,如果存在则删除,删除后再定时
if(m_nHoverTimerID)
{
KillTimer(m_nHoverTimerID);
m_nHoverTimerID=0;
}
m_nHoverTimerID=SetTimer(1,800,NULL);//定时为0.8秒则自动展开
m_HoverPoint=point;
if(m_bDragging)
{
CPointpt=point;
CImageList:
:
DragMove(pt);
//鼠标经过时高亮显示
CImageList:
:
DragShowNolock(false);//避免鼠标经过时留下难看的痕迹
if((hItem=HitTest(point,&flags))!
=NULL)
{
SelectDropTarget(hItem);
m_hItemDragD=hItem;
}
CImageList:
:
DragShowNolock(true);
//当条目被拖曳到左边缘时,将条目放在根下
CRectrect;
GetClientRect(&rect);
if(point.x m_hItemDragD=NULL;
}
CTreeCtrl:
:
OnMouseMove(nFlags,point);
}
voidCXTreeCtrl:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
CTreeCtrl:
:
OnLButtonUp(nFlags,point);
if(m_bDragging)
{
m_bDragging=FALSE;
CImageList:
:
DragLeave(this);
CImageList:
:
EndDrag();
ReleaseCapture();
deletem_pDragImage;
SelectDropTarget(NULL);
if(m_hItemDragS==m_hItemDragD)
{
KillTimer(m_nScrollTimerID);
return;
}
Expand(m_hItemDragD,TVE_EXPAND);
HTREEITEMhtiParent=m_hItemDragD;
//如果是由父节点拖向子节点
while((htiParent=GetParentItem(htiParent))!
=NULL)
{
if(htiParent==m_hItemDragS)
{
//建立一个临时节点以完成操作
HTREEITEMhtiNewTemp=CopyBranch(m_hItemDragS,NULL,TVI_LAST);
HTREEITEMhtiNew=CopyBranch(htiNewTemp,m_hItemDragD,TVI_LAST);
DeleteItem(htiNewTemp);
SelectItem(htiNew);
KillTimer(m_nScrollTimerID);
return;
}
}
HTREEITEMhtiNew=CopyBranch(m_hItemDragS,m_hItemDragD,TVI_LAST);
DeleteItem(m_hItemDragS);
SelectItem(htiNew);
KillTimer(m_nScrollTimerID);
}
}
//拷贝条目
HTREEITEMCXTreeCtrl:
:
CopyItem(HTREEITEMhItem,HTREEITEMhtiNewParent,HTREEITEMhtiAfter)
{
TV_INSERTSTRUCTtvstruct;
HTREEITEMhNewItem;
CStringsText;
//得到源条目的信息
tvstruct.item.hItem=hItem;
tvstruct.item.mask=TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
GetItem(&tvstruct.item);
sText=GetItemText(hItem);
tvstruct.item.cchTextMax=sText.GetLength();
tvstruct.item.pszText=sText.LockBuffer();
//将条目插入到合适的位置
tvstruct.hParent=htiNewParent;
tvstruct.hInsertAfter=htiAfter;
tvstruct.item.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
hNewItem=InsertItem(&tvstruct);
sText.ReleaseBuffer();
//限制拷贝条目数据和条目状态
SetItemData(hNewItem,GetItemData(hItem));
SetItemState( hNewItem,GetItemState(hItem,TVIS_STATEIMAGEMASK),TVIS_STATEIMAGEMASK);
returnhNewItem;
}
//拷贝分支
HTREEITEMCXTreeCtrl:
:
CopyBranch(HTREEITEMhtiBranch,HTREEITEMhtiNewParent,HTREEITEMhtiAfter)
{
HTREEITEMhChild;
HTREEITEMhNewItem=CopyItem(htiBranch,htiNewParent,htiAfter);
hChild=GetChildItem(htiBranch);
while(hChild!
=NULL)
{
CopyBranch(hChild,hNewItem,htiAfter);
hChild=GetNextSiblingItem(hChild);
}
returnhNewItem;
}
voidCXTreeCtrl:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
//处理无意拖曳
m_dwDragStart=GetTickCount();
CTreeCtrl:
:
OnLButtonDown(nFlags,point);
}
voidCXTreeCtrl:
:
OnTimer(UINTnIDEvent)
{
//鼠标敏感节点
if(nIDEvent==m_nHoverTimerID)
{
KillTimer(m_nHoverTimerID);
m_nHoverTimerID=0;
HTREEITEMtrItem=0;
UINTuFlag=0;
trItem=HitTest(m_HoverPoint,&uFlag);
if(trItem&&m_bDragging)
{
SelectItem(trItem);
Expand(trItem,TVE_EXPAND);
}
}
//处理拖曳过程中的滚动问题
elseif(nIDEvent==m_nScrollTimerID)
{
m_TimerTicks++;
CPointpt;
GetCursorPos(&pt);
CRectrect;
GetClientRect(&rect);
ClientToScreen(&rect);
HTREEITEMhItem=GetFirstVisibleItem();
if(pt.y {
//向上滚动
intslowscroll=6-(rect.top+10-pt.y)/20;
if(0==(m_TimerTicks%((slowscroll>0)?
slowscroll:
1)))
{
CImageList:
:
DragShowNolock(false);
SendMessage(WM_VSCROLL,SB_LINEUP);
SelectDropTarget(hItem);
m_hItemDragD=hItem;
CImageList:
:
DragShowNolock(true);
}
}
elseif(pt.y>rect.bottom-10)
{
//向下滚动
intslowscroll=6-(pt.y-rect.bottom+10)/20;
if(0==(m_TimerTicks%((slowscroll>0)?
slowscroll:
1)))
{
CImageList:
:
DragShowNolock(false);
SendMessage(WM_VSCROLL,SB_LINEDOWN);
intnCount=GetVisibleCount();
for(inti=0;i hItem=GetNextVisibleItem(hItem);
if(hItem)
SelectDropTarget(hItem);
m_hItemDragD=hItem;
CImageList:
:
DragShowNolock(true);
}
}
}
else
CTreeCtrl:
:
OnTimer(nIDEvent);
}
通过上面的代码我们就实现了树型控件的拖动操作。
VC程序中树型控件节点拖动的完美实现
2006年04月04日 15:
46天极yesky
作者:
刘涛
VisualC++中提供的MFC类CtreeCtrl(树型控件)用来显示具有一定层次结构的数据项时方便、直观,所以它已经被广泛地应用在各种软件中,如资源管理器中的磁盘目录就用的是树型控件,我们在编程中也会经常用到这个控件,但是这个控件也有缺陷,那就是它并不直接支持拖动节点等高级特性,这使得程序员在编程时使用它受到了很大限制,同时又给软件用户带来了一些不便。
为此,本实例通过从CTreeCtrl中派生了一个类CXTreeCtrl,实现树型控件中节点的拖动。
这个类具有如下的功能:
⑴基本项目条拖动的实现;⑵处理项目条的无意拖动;⑶能处理项目条拖动过程中的滚动问题;⑷拖动过程中节点会智能展开。
程序编译运行后的效果如图所示:
图一:
树型控件节点拖动示例
一、实现方法
我们针对上述自定义类的实现功能,介绍实现思路和方法。
(1)基本项目条拖动的实现
当我们要拖动树型控件的一个项目条时,树型控件会给它的父窗口发送一个TVN_BEGINDRAG通知消息,所以可以在此消息的响应函数中,调用CTreeCtrl:
:
CreateDragImage()函数创建表示当前项目条正处在拖动操作中的图象,该函数创建的图象由项目条的图象和标签文本组成。
创建了拖动图象后,调用CImageList:
:
BeginDrag()函数指定拖动图象的热点位置,然后调用CImageList:
:
DragEnter()函数显示拖动图象。
接下来处理WM_MOUSEMOVE消息用于更新拖动图象,我们想让移动中的图象经过某些项目时高亮度显示,这可以调用CTreeCtrl:
:
SelectDropTarget()来实现。
在调用SelectDropTarget()函数之前,需要先调用CImageList:
:
DragShowNolock(false)函数来隐藏图象列表,然后再调用CImageList:
:
DragShowNolock(true)函数来恢复图象列表的显示,这样就不会在拖动过程中留下难看的轨迹。
最后我们处理WM_LBUTTONUP消息用于完成拖动操作,在该消息响应函数中,我们需要完成结束拖动图象的显示、删除拖动图象、释放鼠标、节点的拷贝/删除等操作。
在节点的拷贝/删除操作中,如果是父节点拖到子节点上,我们可以先将父节点拷到根结点下的临时节点中,再从临时结点处拷到子节点,然后将根结点下的临时节点删除,这样做的目的是防止产生异常。
(2)处理项目条的无意拖动
如果在鼠标按下时不小心移动了鼠标,这时系统就认为产生了一个移动操作,这就产生了误操作。
解决这个问题的方法是设置时间延迟,也就是说当用户按下鼠标后必须在原位置停留一段时间,才能激活拖动操作。
(3)处理拖动过程中的滚动问题
当我们拖动树型控件的项目条时,如果目的节点不可见,则需要拖动滚动条或收拢其它一些节点以使得目的节点显示出来,无疑,这会给我们带来很大的不便。
为此就要给树型控件添加自动滚动支持。
首先设置一个定时器,在WM_TIMER消息中检测鼠标的位置,如果靠近树型控件的下边缘,则使得控件向下滚动。
靠近上边缘则向上滚动。
滚动速度根据鼠标的位置确定。
(4)拖动过程中节点的智能展开
为了实现在拖动过程中鼠标停留在某个节点上一段时间后,该节点会自动展开的功能。
设置一个定时器,当鼠标在拖动过程中停止在某个节点上时,定时器被启动,再设置一变量保存当前的鼠标位置。
二、编程步骤
1、新建一对话框工程DragTree,编辑资源,在对话框中加入一树型控件IDC_TREE,属性设置为:
HasButtons、HasLines、Linesatroot、EditLabels、Border;
2、使用ClassWizard给该控件添加一个成员变量m_wndTree,在代码部分将该控件的类型修改为CXTreeCtrl。
3、在对话框的OnInitDialog()函数中添加代码,初始化树型控件的项目条;
4、制作一个图像资源(ID为IDB_TREEIMAGE),其中包含两个小图标,用来作为树型控件项目条的显示图标;
5、添加代码,编译运行程序。
三、程序代码
//XTreeCtrl.h:
headerfile
#if!
defined(AFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_)
#defineAFX_XTREECTRL_H__3EF12526_EF66_4FD9_A572_59476441D79A__INCLUDED_
#if_MSC_VER>1000
#pragmaonce
#endif//_MSC_VER>1000
classCXTreeCtrl:
publicCTreeCtrl
{
//Construction
public:
CXTreeCtrl();
//Attributes
public:
//Operations
public:
//Overrides
//ClassWizardgeneratedvirtualfunctionoverrides
//{{AFX_VIRTUAL(CXTreeCtrl)
//}}AFX_VIRTUAL
//Implementation
public:
virtual~CXTreeCtrl();
//Generatedmessagemapfunctions
protected:
UINTm_TimerTicks;//处理滚动的定时器所经过的时间
UINTm_nScrollTimerID;//处理滚动的定时器
CPointm_HoverPoint;//鼠标位置
UINTm_nHoverTimerID;//鼠标敏感定时器
DWORDm_dwDragStart;//按下鼠标左键那一刻的时间
BOOLm_bDragging;//标识是否正在拖动过程中
CImageList*m_pDragImage;//拖动时显示的图象列表
HTREEITEMm_hItemDragS;//被拖动的标签
HTREEITEMm_hItemDragD;//接受拖动的标签
//{{AFX_MSG(CXTreeCtrl)
afx_msgvoidOnBegindrag(NMHDR*pNMHDR,LRESULT*pResult);
afx_msgvoidOnMouseMove(UINTnFlags,CPointpoint);
afx_msgvoidOnLButtonUp(UINTnFlags