通过从客户区的一部分拖动来移动无框 window
Moving frameless window by dragging it from a portion of client area
正如标题所说,我只想在用户将其从客户区的一部分拖动时才移动 window。这将是对正常标题栏移动的模仿,这是因为我的表单是自定义的,它没有任何标题或标题栏。目前,我使用的代码如下:
...
case WM_NCHITTEST:
return HTCAPTION;
这对于让用户能够移动 window 无论他从哪里拖动都很好。我想限制这种可能性(只有 window 的顶部允许移动)。我没有尝试检查鼠标按下的位置,因为我不知道如何在 WM_NCHITTEST
消息中进行检查。
我在 Visual Studio 2015 年使用纯 Win32 (winapi) C 代码(目前没有 MFC 或其他任何东西)。
您可以在 WM_LBUTTONDOWN 处理程序中调用一些魔术代码,AFAIR 这个:
ReleaseCapture();
SendMessage(yourWindowHandle, WM_SYSCOMMAND, 0xf012, 0) ;
我几年前在 Delphi 和 Windows XP 中使用过这种方法。我认为它对于 c++ 来说一定是相似的。当然,您可以在执行此操作之前检查 x 和 y。
如果您只是 return HTCAPTION
回复 所有 WM_NCHITTEST
消息,您将 运行 惹上麻烦。您会破坏滚动条、关闭按钮、调整边框大小等,这些都是通过不同的 HT*
值实现的。
不过你的想法是对的。您想让 window 的客户区可拖动,因此您需要让 Windows 认为您的客户区实际上是标题区(如您所知,它是可拖动的)。该代码如下所示:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
case WM_NCHITTEST:
{
// Call the default window procedure for default handling.
const LRESULT result = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
// You want to change HTCLIENT into HTCAPTION.
// Everything else should be left alone.
return (result == HTCLIENT) ? HTCAPTION : result;
}
// ...
}
但是,根据您问题中的图像,您似乎只想将其限制在 window 的特定区域。您将需要准确定义该区域是什么,然后 hit-test 以查看用户是否单击了该区域。假设 rcDraggable
是一个 RECT
结构,其中包含 the red box shown in your image 的边界(在屏幕坐标中),您可以使用以下代码:
static RECT rcDraggable = ...
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
case WM_NCHITTEST:
{
// Call the default window procedure for default handling.
const LRESULT result = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
// Get the location of the mouse click, which is packed into lParam.
POINT pt;
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
// You want to change HTCLIENT into HTCAPTION for a certain rectangle, rcDraggable.
// Everything else should be left alone.
if ((result == HTCLIENT) && (PtInRect(&rcDraggable, pt))
{
return HTCAPTION;
}
return result;
}
// ...
}
如果您根据客户端坐标定义 rcDraggable
,则需要在执行 hit-test 响应 WM_NCHITTEST
之前将其转换为屏幕坐标。为此,请调用 MapWindowPoints
function,如下所示:
RECT rc = rcDraggable;
MapWindowPoints(hWnd, /* a handle to your window */
NULL, /* convert to screen coordinates */
reinterpret_cast<POINT*>(&rc),
(sizeof(RECT) / sizeof(POINT)));
正如标题所说,我只想在用户将其从客户区的一部分拖动时才移动 window。这将是对正常标题栏移动的模仿,这是因为我的表单是自定义的,它没有任何标题或标题栏。目前,我使用的代码如下:
...
case WM_NCHITTEST:
return HTCAPTION;
这对于让用户能够移动 window 无论他从哪里拖动都很好。我想限制这种可能性(只有 window 的顶部允许移动)。我没有尝试检查鼠标按下的位置,因为我不知道如何在 WM_NCHITTEST
消息中进行检查。
我在 Visual Studio 2015 年使用纯 Win32 (winapi) C 代码(目前没有 MFC 或其他任何东西)。
您可以在 WM_LBUTTONDOWN 处理程序中调用一些魔术代码,AFAIR 这个:
ReleaseCapture();
SendMessage(yourWindowHandle, WM_SYSCOMMAND, 0xf012, 0) ;
我几年前在 Delphi 和 Windows XP 中使用过这种方法。我认为它对于 c++ 来说一定是相似的。当然,您可以在执行此操作之前检查 x 和 y。
如果您只是 return HTCAPTION
回复 所有 WM_NCHITTEST
消息,您将 运行 惹上麻烦。您会破坏滚动条、关闭按钮、调整边框大小等,这些都是通过不同的 HT*
值实现的。
不过你的想法是对的。您想让 window 的客户区可拖动,因此您需要让 Windows 认为您的客户区实际上是标题区(如您所知,它是可拖动的)。该代码如下所示:
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
case WM_NCHITTEST:
{
// Call the default window procedure for default handling.
const LRESULT result = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
// You want to change HTCLIENT into HTCAPTION.
// Everything else should be left alone.
return (result == HTCLIENT) ? HTCAPTION : result;
}
// ...
}
但是,根据您问题中的图像,您似乎只想将其限制在 window 的特定区域。您将需要准确定义该区域是什么,然后 hit-test 以查看用户是否单击了该区域。假设 rcDraggable
是一个 RECT
结构,其中包含 the red box shown in your image 的边界(在屏幕坐标中),您可以使用以下代码:
static RECT rcDraggable = ...
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ...
case WM_NCHITTEST:
{
// Call the default window procedure for default handling.
const LRESULT result = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
// Get the location of the mouse click, which is packed into lParam.
POINT pt;
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
// You want to change HTCLIENT into HTCAPTION for a certain rectangle, rcDraggable.
// Everything else should be left alone.
if ((result == HTCLIENT) && (PtInRect(&rcDraggable, pt))
{
return HTCAPTION;
}
return result;
}
// ...
}
如果您根据客户端坐标定义 rcDraggable
,则需要在执行 hit-test 响应 WM_NCHITTEST
之前将其转换为屏幕坐标。为此,请调用 MapWindowPoints
function,如下所示:
RECT rc = rcDraggable;
MapWindowPoints(hWnd, /* a handle to your window */
NULL, /* convert to screen coordinates */
reinterpret_cast<POINT*>(&rc),
(sizeof(RECT) / sizeof(POINT)));