【发布时间】:2013-08-04 18:59:40
【问题描述】:
给定一个 Win32 窗口的句柄,我需要找到它相对于其父窗口的位置。
我知道几个函数(例如;GetWindowRect() 和 GetClientRect()),但它们都没有明确返回所需的坐标。
我该怎么做?
【问题讨论】:
标签: winapi position window win32gui
给定一个 Win32 窗口的句柄,我需要找到它相对于其父窗口的位置。
我知道几个函数(例如;GetWindowRect() 和 GetClientRect()),但它们都没有明确返回所需的坐标。
我该怎么做?
【问题讨论】:
标签: winapi position window win32gui
解决方案是使用GetWindowRect() 和MapWindowPoints() 的组合力量。
GetWindowRect() 检索窗口相对于您在监视器上看到的整个屏幕区域的坐标。我们需要将这些绝对坐标转换为我们主窗口区域的相对坐标。 MapWindowPoints() 将给定的相对于一个窗口的坐标转换为相对于另一个窗口的坐标。所以我们需要一个屏幕区域的“句柄”和我们试图找到坐标的控件的父窗口的句柄。屏幕是 Windows 术语中的“窗口”,称为“桌面”。我们可以通过WinUser.h中定义的常量HWND_DESKTOP访问Desktop的句柄(包括Windows.h就够了)。我们可以通过调用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) 相对于其父窗口的相对坐标。
【讨论】:
SetCursorPos 自己尝试一下,看看鼠标光标落在哪里。
CWnd 的子类(比如CDialog),你可能需要在系统函数调用前加上:: 前缀,比如::MapWindowPoints(...)
这是我用于窗口或控件(子窗口)的解决方案
RECT rc;
GetClientRect(hWnd,&rc);
MapWindowPoints(hWnd,GetParent(hWnd),(LPPOINT)&rc,2);
【讨论】:
MapWindowPoints 的最后一个参数是要映射的 points 的数量。事实上,如果你不通过 2,那么在镜像(从右到左)窗口的情况下,该函数可能不会达到你期望的效果。请参阅remarks in the documentation。
这是一个基于上述答案的非常基本的 typedef 结构:
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 扩展到一系列控件,其中相应的rectOuts 的存储可以考虑用于更适应的结构。
用法:以下返回转换后的rect:
RECT rect1 = RectCl().RectCl(IDC_CTRL1, hWndCtrl, hWndOwner);
以下是一个部分编码的函数,它将两个答案的元素转化为可用于对话框的东西 - 特别是对于移动/调整控制坐标,这是登陆此页面的最初原因。
它接受来自资源的完整控件 ID 或来自 CreateWindow 的 HMENU 项及其容器的句柄作为参数。
还应该考虑在调用函数之前是否通过在WM_SIZE 中侦听SIZE_MINIMIZED 来最小化ownerHwnd。
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;
}
【讨论】:
我知道之前已经回答过了,但是在屏幕坐标中获取子窗口的矩形、获取它的位置 (POINT ptCWPos = {rectCW.left, rectCW.top};) 并使用 ScreenToClient() 函数要容易得多,这会将屏幕坐标点转换为窗口的客户端坐标点:
PS:我知道这看起来有很多代码,但大部分都在摆弄 rect 位置;在大多数情况下,实际上需要的是 rect 位置而不是整个 rect。
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
【讨论】:
MapWindowPoints上的备注。
更简单
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);
}
【讨论】: