我被要求为现有程序添加新功能。该程序由一个没有标题/边框的对话框组成。我需要两样东西:
- 当用户简单地点击对话框区域时,只需关闭它
- 当用户将鼠标移到对话框区域内并拖动时,移动对话框
以下是我迄今为止的发现:
void MyDialog::onMessageReceived(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_LBUTTONDOWN:
lastX=LOWORD(lParam);
lastY=HIWORD(lParam);
SendMessage(DlgHandle, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;
case WM_LBUTTONUP:
if (LOWORD(lParam)==lastX && HIWORD(lParam)==lastY)
onKillButtonClick();
break;
}}
编辑:这个函数是这样调用的:
INT_PTR CALLBACK MyDialog::dialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
_this->onMessageReceived(uMsg, wParam, lParam);
}
移动窗口效果很好,但看起来WM_LBUTTONUP事件丢失了。我不得不点击两次才能把它解雇。希望有人能帮我…
编辑:使用Spy++,我看到WM_LBTTONUP被触发,但在发出新的WM_NCLBUTTONDOWN之后立即触发。
首先,我同意Michael Walz的观点-这是一个非常令人困惑的行为:鼠标向上事件的处理取决于它是否移动。。。如果它只是移动了一点点呢?我更愿意用一个不同的操作来关闭这个对话框——点击图标,右键单击,等等。
但是,让用户移动无字幕窗口的正确方法是处理WM_NCHITTEST
消息并返回HTCAPTION
:
case WM_NCHITTEST:
SetWindowLong(hDlg, DWL_MSGRESULT, HTCAPTION);
return HTCAPTION;
不幸的是,Windows将接管所有鼠标事件,因此,正如您所观察到的,您永远不会得到WM_LBUTTONUP
。你可以选择设置一个短计时器,看看用户是否开始移动你的窗口;当您收到WM_ENTERSIZEMOVE
消息时取消它。如果计时器启动,关上你的窗户。是的,这也很尴尬,但不再是你的求婚。
另一种方法是自己处理移动:
static bool bDragging(false), bMoved(false);
static POINT pt1 = {}, pt2 = {};
static RECT r;
switch (message)
{
case WM_LBUTTONDOWN:
GetWindowRect(hDlg, &r);
pt1 = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen(hDlg, &pt1);
bDragging = true;
bMoved = false;
break;
case WM_MOUSEMOVE:
if (bDragging)
{
pt2 = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ClientToScreen(hDlg, &pt2);
if (pt2.x != pt1.x || pt2.y != pt1.y)
{
OffsetRect(&r, pt2.x - pt1.x, pt2.y - pt1.y);
SetWindowPos(hDlg, 0, r.left, r.top, 0, 0, SWP_NOSIZE);
pt1 = pt2;
bMoved = true;
}
}
break;
case WM_LBUTTONUP:
bDragging = false;
if (!bMoved)
PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0);
bMoved = false;
break;
}
return (INT_PTR)FALSE;