通过从客户区的一部分拖动来移动无框 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)));