解决方案是结合使用GetWindowRect()
和MapWindowPoints()
的能力。
GetWindowRect()
检索窗口相对于您在监视器上看到的整个屏幕区域的坐标。我们需要将这些绝对坐标转换为我们主窗口区域的相对坐标。 MapWindowPoints()
将给定相对于一个窗口的坐标转换为相对于另一个窗口的坐标。因此,我们需要屏幕区域的“句柄”以及正在尝试查找其坐标的控件的父窗口的句柄。在Windows术语中,屏幕区域是一个“窗口”,称为“桌面”。我们可以通过常量HWND_DESKTOP
(包括Windows.h
)中定义的方式访问Desktop的句柄。我们可以通过调用Win32函数GetParent()
来轻松获取父窗口的句柄。现在我们拥有调用MapWindowPoints()
函数所需的所有参数。
RECT YourClass::GetLocalCoordinates(HWND hWnd) const
{
RECT Rect;
GetWindowRect(hWnd, &Rect);
MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), (LPPOINT) &Rect, 2);
return Rect;
}
MapWindowPoints()
的定义如下:
int MapWindowPoints(
_In_ HWND hWndFrom,
_In_ HWND hWndTo,
_Inout_ LPPOINT lpPoints,
_In_ UINT cPoints
);
MapWindowPoints()
函数将坐标从 hWndFrom
转换为相对于 hWndTo
的坐标。在我们的情况下,我们是从桌面 (HWND_DESKTOP
) 转换到我们的父窗口 (GetParent(hWnd)
)。因此,生成的 RECT
结构保存了子窗口 (hWnd
) 相对于其父窗口的相对坐标。
这就是我在Windows或控件(子窗口)上使用的解决方案。
RECT rc;
GetClientRect(hWnd,&rc);
MapWindowPoints(hWnd,GetParent(hWnd),(LPPOINT)&rc,2);
MapWindowPoints
的最后一个参数需要映射的点数。事实上,如果你不传递2,对于镜像(从右到左)的窗口,该函数可能无法按照你的预期工作。请参阅文档中的备注。 - Rob KennedyPOINT ptCWPos = {rectCW.left, rectCW.top};
) 并使用 ScreenToClient()
函数,将屏幕坐标点转换为窗口客户端坐标点:HWND hwndCW, hwndPW; // the child window hwnd
// and the parent window hwnd
RECT rectCW;
GetWindowRect(hwndCW, &rectCW); // child window rect in screen coordinates
POINT ptCWPos = { rectCW.left, rectCW.top };
ScreenToClient(hwndPW, &ptCWPos); // transforming the child window pos
// from screen space to parent window space
LONG iWidth, iHeight;
iWidth = rectCW.right - rectCW.left;
iHeight = rectCW.bottom - rectCW.top;
rectCW.left = ptCWPos.x;
rectCW.top = ptCWPos.y;
rectCW.right = rectCW.left + iWidth;
rectCW.bottom = rectCW.right + iHeight; // child window rect in parent window space
typedef struct tagRectCl {
static RECT rectOut;
RECT RectCl(int ctrlID, HWND &hwndCtrl, HWND &ownerHwnd)
{
RECT rectIn;
GetWindowRect(hwndCtrl, &rectIn); //get window rect of control relative to screen
MapWindowPoints(NULL, ownerHwnd, (LPPOINT)&rectIn, 2);
rectOut = rectIn;
return rectOut;
}
RECT RectCl(int ctrlID)
{
// for rectOut already populated
return rectOut;
}
};
}RectCl;
RECT RectCl::rectOut = {};
这个想法是将ctrlID扩展到一系列控件上,对应的rectOut
的存储可以考虑更适合的结构。
用法:以下代码返回转换后的矩形:
RECT rect1 = RectCl().RectCl(IDC_CTRL1, hWndCtrl, hWndOwner);
CreateWindow
中的HMENU
项的整数控件ID作为参数,并接受其容器的句柄。ownerHwnd
是否被最小化,通过侦听WM_SIZE
中的SIZE_MINIMIZED
来判断。BOOL ProcCtrl(int ctrlID, HWND ownerHwnd)
{
RECT rcClient = {0};
HWND hwndCtrl = GetDlgItem(ownerHwnd, ctrlID);
if (hwndCtrl)
{
GetWindowRect(hwndCtrl, &rcClient); //get window rect of control relative to screen
MapWindowPoints(NULL, ownerHwnd, (LPPOINT)&rcClient,2);
/* Set extra scaling parameters here to suit in either of the following functions
if (!MoveWindow(hwndCtrl, rcClient.left, rcClient.top,
rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, TRUE))
{
//Error;
return FALSE;
}
//if (!SetWindowPos(hwndCtrl, NULL, (int)rcClient.left, (int)(rcClient.top),
//(int)(rcClient.right - rcClient.left), (int)(rcClient.bottom - rcClient.top), SWP_NOZORDER))
{
//Error;
//return FALSE;
}
}
else
{
//hwndCtrl Error;
return FALSE;
}
return TRUE;
}
更简单
BOOL CAuxFormView::OnInitDialog()
{
// run the default
CDialog::OnInitDialog();
// define a rectangular
CRect rc1;
// get area of the control to the screen coordinates
m_Viewer_Ctrl.GetWindowRect(rc1);
// transform the screen coordinates relevant to the Dialog coordinates
ScreenToClient(rc1);
// move and refresh the control to the new area
m_Viewer_Ctrl.MoveWindow(rc1);
}
SetCursorPos
自己尝试一下,并查看鼠标指针落在哪里。 - RectangleEqualsCWnd
(如CDialog
)的类中使用它,您可能想在系统函数调用前加上::
前缀,例如::MapWindowPoints(...)
。 - Gunther Struyf