本篇笔记实现一个可在父窗口中自由拖动的按钮(CButton),并且不允许拖出父窗口(这样就拖不回来了)。
新建类CMovableButton,继承CButton。为CMovableButton添加成员变量bMoving_,并添加WM_LBUTTONDOWN、WM_LBUTTONUP消息处理函数。
void CMovableButton::OnLButtonDown(UINT nFlags, CPoint point) { bMoving_ = true; CButton::OnLButtonDown(nFlags, point); } void CMovableButton::OnLButtonUp(UINT nFlags, CPoint point) { bMoving_ = false; CButton::OnLButtonUp(nFlags, point); }
bMoving_在构造函数中初始化为false,用于标记是否正在移动按钮。在OnLButtonDown()中置为true,在OnLButtonUp()中置为false。
为CMovableButton添加WM_MOUSEMOVE消息处理函数,在OnMouseMove()中判断如果bMoving_为true,则想办法移动自己。方法有两种,一种是发消息,另一种是直接调用SetWindowPos(MoveWindow)修改窗口位置。具体看代码:
void CMovableButton::OnMouseMove(UINT nFlags, CPoint point) { if (bMoving_) { #if 1 //Method 1 ReleaseCapture(); //Sets dragging of full windows either on or off. //The uiParam parameter specifies TRUE for on, or FALSE for off. //::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, 0); //SendMessage(WM_SYSCOMMAND, /*0xF012*/SC_MOVE|HTCAPTION, 0); PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y)); #else //Method 2 ClientToScreen(&point); GetParent()->ScreenToClient(&point); CRect rcButton; GetClientRect(&rcButton); point.Offset(-rcButton.Width()/2, -rcButton.Height()/2); SetWindowPos(NULL, point.x, point.y, 0, 0, SWP_NOZORDER|SWP_NOSIZE); //Invalidate(); #endif } CButton::OnMouseMove(nFlags, point); }
这样按钮就可以自由拖动了,还需要限制按钮的拖动范围,否则一旦拖出父窗口外面就拖不回来了。为CMovableButton添加WM_WINDOWPOSCHANGING消息处理函数,作如下处理:
// 防止子窗口移出父窗口 void CMovableButton::OnWindowPosChanging(WINDOWPOS* lpwndpos) { CRect rcParent; GetParent()->GetClientRect(&rcParent); CRect rcButton; GetClientRect(&rcButton); if (lpwndpos->x < rcParent.left) lpwndpos->x = rcParent.left; if (lpwndpos->y < rcParent.top) lpwndpos->y = rcParent.top; if (lpwndpos->x + rcButton.Width() > rcParent.right) lpwndpos->x = rcParent.right - rcButton.Width(); if (lpwndpos->y + rcButton.Height() > rcParent.bottom) lpwndpos->y = rcParent.bottom - rcButton.Height(); }
这样CMovableButton就完成了,试了一下,可以满足我的需求。这种实现思路同样适用于其他种类的无标题栏的子窗口拖动。