2007年5月26日土曜日

树控件消息

树控件一共有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 件のコメント: