树控件一共有43个控制消息(TVM_XXX)和23个通知消息(TVN_XXX)下面按操作的不同进行简单的说明。
一、 一、拖拽消息
拖拽(drag-drop)操作包含这样几步:
1. 1。选中一个节点后,按住鼠标左键(此时是左键拖拽)开始拖动鼠标时,树控件向其父窗口发送TVN_BEGINDRAG通知消息(通过此消息可以得到被拖拽节点的句柄)。此时,需要准备在拖拽操作时所显示的拖拽图标。那么需要向树控件发送TVM_CREATEDRAGIMAGE消息,它创建一个Image List对象,并把被选中节点的图标复制到这个Image List中,并返回Image List句柄。然后使用ImageList_BeginDrag以及ImageList_DragEnter宏对这个Image List做一些设置,比如拖拽时鼠标所处的作标点。
case TVN_BEGINDRAG:
{
POINT pt;
pt.x = 0;
pt.y = 0;
ClientToScreen(GetDlgItem(hWnd, ID_TREEVIEW), &pt);
NMTREEVIEW *lpnmtv = (NMTREEVIEW*) lParam;
hitemDrag = lpnmtv->itemNew.hItem;
hitemDrop = NULL;
himge = (HIMAGELIST)SendMessage(GetDlgItem(hWnd, ID_TREEVIEW), TVM_CREATEDRAGIMAGE,0,(LPARAM)(HTREEITEM)lpnmtv->itemNew.hItem);
if(!himge)
break;
bdragging = TRUE;
ImageList_BeginDrag(himge, 0, pt.x ,pt.y );
POINT point = lpnmtv->ptDrag;
ClientToScreen(GetDlgItem(hWnd, ID_TREEVIEW), &point);
ImageList_DragEnter(GetDlgItem(hWnd, ID_TREEVIEW),point.x ,point.y );
SetCapture(hWnd);
break;
}
2.2。 拖拽过程中会产生一系列的WM_MOUSEMOVE消息,因此要在这个消息的处理例程中要确定拖拽图标的位置(ImageList_DragMove())以及拖拽目标的位置。对于拖拽目标的确定,一般使用TVM_HITTEST消息来获得拖拽目标节点的句柄。并把这个节点加亮显示(使用带TVGN_DROPHILITE标记的TVM_SELECTITEM消息可以加亮显示拖拽节点)。
case WM_MOUSEMOVE:
{
TVHITTESTINFO tvht;
UINT flags;
flags = (UINT)wParam;
if(bdragging){
pos.x = LOWORD(lParam);
pos.y = HIWORD(lParam);
ClientToScreen(GetDlgItem(hWnd, ID_TREEVIEW), &pos);
ImageList_DragMove(pos.x, pos.y);
ImageList_DragShowNolock(FALSE);
ScreenToClient(GetDlgItem(hWnd, ID_TREEVIEW), &pos);
tvht.pt.x = pos.x;
tvht.pt.y = pos.y;
if(hitemDrop = (HTREEITEM)SendMessage(GetDlgItem(hWnd, ID_TREEVIEW),
TVM_HITTEST, (WPARAM)&flags ,(LPARAM)&tvht))
{
SendMessage(GetDlgItem(hWnd, ID_TREEVIEW), TVM_SELECTITEM,
TVGN_DROPHILITE,(LPARAM)hitemDrop);
}
ImageList_DragShowNolock(TRUE);
}
break;
}
3.3。 最后选定拖拽目标节点后,释放鼠标键产生WM_LBUTTONUP消息。在这里,Image List已完成它的任务,应该释放它(ImageList_DragLeave 、ImageList_EndDrag)。然后将源节点插入到目标节点处,并删除源节点。
case WM_LBUTTONUP:
{
if (bdragging){
bdragging = FALSE;
ImageList_DragLeave(GetDlgItem(hWnd, ID_TREEVIEW));
ImageList_EndDrag();
ReleaseCapture();
if( hitemDrag == hitemDrop )
break;
HTREEITEM htiParent = hitemDrop;
while( (htiParent = (HTREEITEM)SendMessage(GetDlgItem(hWnd, ID_TREEVIEW),
TVM_GETNEXTITEM,
(WPARAM)(UINT)TVGN_PARENT,
(LPARAM)htiParent )) != NULL )
{
if( htiParent == hitemDrag )
break;
}
DeleteObject(himge);
SendMessage(GetDlgItem(hWnd, ID_TREEVIEW), TVM_EXPAND,
(WPARAM) (UINT)TVE_EXPAND, (LPARAM)hitemDrop);
HTREEITEM htiNew = CopyBranch(GetDlgItem(hWnd, ID_TREEVIEW),
hitemDrag, hitemDrop, TVI_LAST );
SendMessage(GetDlgItem(hWnd, ID_TREEVIEW), TVM_DELETEITEM,
0, (LPARAM)hitemDrag);
/*检查最后高亮显示的节点,并选中它。还必须使得其不再高亮显示,否则其它的节点被选中时就不能高亮显示了。*/
SendMessage(GetDlgItem(hWnd, ID_TREEVIEW), TVM_SELECTITEM,
TVGN_DROPHILITE,(LPARAM)0);
SendMessage(GetDlgItem(hWnd, ID_TREEVIEW), TVM_SELECTITEM,
TVGN_CARET, (LPARAM)htiNew);
}
break;
}
为了将源节点插入到目标节点处,还需要进行插入操作。为此需要两个函数如下:
char Text[128];
HTREEITEM CopyItem(HWND htreewnd, HTREEITEM hItem, HTREEITEM htiNewParent, HTREEITEM htiAfter /*= TVI_LAST*/ )
{
TV_INSERTSTRUCT tvstruct;
HTREEITEM hNewItem;
// 获得源节点信息
tvstruct.item.hItem = hItem;
tvstruct.item.mask = TVIF_CHILDREN | TVIF_HANDLE |
TVIF_IMAGE | TVIF_SELECTEDIMAGE ;
SendMessage(htreewnd, TVM_GETITEM, 0 ,(LPARAM)&tvstruct.item);
// 在适当的位置上插入节点
tvstruct.hParent = htiNewParent;
tvstruct.hInsertAfter = htiAfter;
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
tvstruct.item.pszText = Text;
tvstruct.item.cchTextMax = 128;
SendMessage(htreewnd, TVM_GETITEM, 0 ,(LPARAM)&tvstruct.item);
hNewItem = (HTREEITEM)SendMessage(htreewnd, TVM_INSERTITEM, 0, (LPARAM)&tvstruct);
// 复制节点数据和状态
TVITEM item;
item.mask = TVIF_PARAM | TVIF_HANDLE | TVIF_STATE;
item.hItem = hItem;
item.stateMask = TVIS_STATEIMAGEMASK;
item.state = 0;
SendMessage(htreewnd, TVM_GETITEM, 0, (LPARAM)&item);
item.hItem = hNewItem;
SendMessage(htreewnd, TVM_SETITEM, 0, (LPARAM)&item);
return hNewItem;
}
HTREEITEM CopyBranch(HWND htreewnd, HTREEITEM htiBranch, HTREEITEM htiNewParent, HTREEITEM htiAfter )
{
HTREEITEM hChild;
HTREEITEM hNewItem = CopyItem(htreewnd, htiBranch, htiNewParent, htiAfter);
hChild = (HTREEITEM)SendMessage(htreewnd, TVM_GETNEXTITEM, TVGN_CHILD,
(LPARAM) htiBranch);
while( hChild != NULL)
{
// 递归地移动所有节点
CopyBranch(htreewnd, hChild, hNewItem, htiAfter);
hChild = (HTREEITEM)SendMessage(htreewnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hChild);
}
return hNewItem;
}
0 件のコメント:
コメントを投稿