C++ 获取对话框控件的位置和大小
C++ Get the position and size of a dialog control
我正在使用 Visual Studio 2015 C++ 并在 .rc 文件中定义了一个对话框:
IDD_SERIALCTRLDEMO_DIALOG DIALOGEX 0, 0, 313, 164
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "SerialCtrlDemo"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
LTEXT "Serial Number:",IDC_STATIC,14,10,48,8
COMBOBOX IDC_COMBO_SN,66,8,48,55,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Baud Rate:",IDC_STATIC,135,10,37,8
COMBOBOX IDC_COMBO_BR,176,8,48,55,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Read:",IDC_STATIC,11,51,20,8
PUSHBUTTON "Open",IDC_BUTTON_OPEN,242,7,50,14
LISTBOX IDC_LIST_READ,11,62,23,76,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
EDITTEXT IDC_EDIT_WRITE,11,34,237,14,ES_AUTOHSCROLL
PUSHBUTTON "Write",IDC_BUTTON_WR,252,33,46,14
GROUPBOX "",IDC_STATIC,7,0,294,25
GROUPBOX "",IDC_STATIC,7,26,296,117
LTEXT "",IDC_STATIC_INFO,11,150,287,8
CONTROL "",IDC_SENSOR,"Static",SS_BLACKRECT,41,62,256,76
END
enter code here
我感兴趣的控件是图片框"IDC_SENSOR"(最后一个条目),它被定义为at/as 41,62,256,76。但在屏幕上比例不同,可能是由于动态对话框布局。
所以我想知道对话框中那个框的确切位置和大小,但我无法获取这些参数。
使用的程序:
void CSerialCtrlDemoDlg::OnEventRead(char *inPacket,int inLength)
}
int x=0;
m_listboxRead.AddString(inPacket); // display the incomming data in the 'Read' listbox
CString str; // display the # of data's in 'Info'
str.Format("%d bytes read",inLength);
m_staticInfo.SetWindowText(str);
CWnd *cWndSensor = GetDlgItem(IDC_SENSOR); // show the fluctuation in the Picturebox
CDC *pDC = cWndSensor->GetDC();
for (x = 0; x < 200; x++)
{
pDC->SetPixelV(x, 10, RGB(rand() % 255, 0, 0));
}
}
for循环中的x<200应该设置为图片框的宽度。
如何获得这些尺寸?
我希望这些信息足够了(?)。
你好,CJ。
The control I'm interested in is the picturebox "IDC_SENSOR" (last entry), which is defined at/as 41,62,256,76. But on screen that proportions are different, probably due to the Dynamic Dialog Layout.
是的,没错。对话资源中的坐标在 DLU(对话单位)中指定。您可以通过调用 MapDialogRect
函数将对话框单位转换为像素。
但这并不是您想在此处执行的操作,因为您不想 hard-code 代码中的对话框坐标。如果您更改资源文件,您希望您的代码继续工作。
I want to know the exact position and size of that box whithin the dialogbox
你真正需要的是获取控件在屏幕上的坐标,以像素为单位。有两个专门为此目的设计的函数:
GetClientRect
告诉您 window 在 client 坐标中的矩形。对于一个childwindow,比如一个控件,upper-left角永远是(0, 0),lower-right角会告诉你宽度和高度。
GetWindowRect
告诉您 window 在 屏幕 坐标中的矩形。这些是相对于虚拟屏幕的 "absolute" 坐标。与客户端矩形不同,window 矩形还包括 window 的 non-client 区域的大小。
您可能已经知道这一点,但此矩形的宽度是通过从其右边界减去其左边界来确定的。类似地,高度是通过从其底部边界减去其顶部边界来确定的。或者,如果您使用 MFC 包装器 class、CRect
(它在任何地方都可以与 RECT
互换,包括在调用上述两个函数时),您可以只调用 Width
和 Height
个成员函数。
至于您的特定 use-case,因为您在 child window 本身上调用 GetDC
,您将获得一个客户端 DC,并且您会需要客户坐标,所以你会调用 GetClientRect
。简单的。除了你的代码是错误的。您正在获取一个 DC 并在 WM_PAINT
消息处理程序的 外部 绘制,这意味着您绘制的任何内容都可能会以任意间隔被擦除。所有绘图都应响应 WM_PAINT
消息完成。
在您的情况下完成此操作的一种简单方法是为 IDC_SENSOR
控件设置 SS_OWNERDRAW
样式。这使 parent 对话框负责绘制控件的内容。当需要绘制 child 控件时,它会收到 WM_DRAWITEM
消息。添加一个 OnDrawItem
处理程序并在其中进行绘图。作为参数传递的 DRAWITEMSTRUCT
将告诉您应该绘制的矩形 (rcItem
)。
最后,我要注意 SetPixelV
相当慢。如果它对您来说足够快,请忽略此评论的其余部分。但是如果在屏幕上绘图真的很慢,那么你应该考虑创建并缓存位图object。绘制到该位图中,设置其各个像素,然后将该位图简单地 blit 到控件的设备上下文中。访问和操作屏幕外位图的单个像素 快得多。
我已成功使用 GetClientRect 检索到详细信息:
void CSerialCtrlDemoDlg::OnEventRead(char *inPacket,int inLength)
{
int i = 0;
int j = 0;
int x = 0;
int y = 0;
int w = 100;
int h = 50;
RECT sensorRect;
CWnd *cWndSensor = GetDlgItem(IDC_SENSOR);
CDC *pDC = cWndSensor->GetDC();
cWndSensor->GetClientRect(&sensorRect);
x = sensorRect.left;
y = sensorRect.top;
w = sensorRect.right - x;
h = sensorRect.bottom - y;
for (i = 0; i < w; i++) // this fills the picturebox (for test only)
{
for (j = 0; j < h; j++)
{
pDC->SetPixelV(i, j, RGB(rand() % 255, 0, 0));
}
}
}
所以我将此问题标记为已回答!
我不太确定这是否是 'correct' 方式,我缺乏这方面的经验。但它有效,然后它应该相当不错:)
我将按照建议使用位图,而不是(直接)绘制到图片框中。那确实更好,更容易使用。
我现在需要发现的是如何使用该位图以正确的方式更新对话框。正如科迪所说,此类绘图不应在此例程中完成,而应作为单独的 OnDrawItem 指令处理。嗯,还有很多要学。
谢谢你,CJ
我正在使用 Visual Studio 2015 C++ 并在 .rc 文件中定义了一个对话框:
IDD_SERIALCTRLDEMO_DIALOG DIALOGEX 0, 0, 313, 164
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "SerialCtrlDemo"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
LTEXT "Serial Number:",IDC_STATIC,14,10,48,8
COMBOBOX IDC_COMBO_SN,66,8,48,55,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Baud Rate:",IDC_STATIC,135,10,37,8
COMBOBOX IDC_COMBO_BR,176,8,48,55,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Read:",IDC_STATIC,11,51,20,8
PUSHBUTTON "Open",IDC_BUTTON_OPEN,242,7,50,14
LISTBOX IDC_LIST_READ,11,62,23,76,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
EDITTEXT IDC_EDIT_WRITE,11,34,237,14,ES_AUTOHSCROLL
PUSHBUTTON "Write",IDC_BUTTON_WR,252,33,46,14
GROUPBOX "",IDC_STATIC,7,0,294,25
GROUPBOX "",IDC_STATIC,7,26,296,117
LTEXT "",IDC_STATIC_INFO,11,150,287,8
CONTROL "",IDC_SENSOR,"Static",SS_BLACKRECT,41,62,256,76
END
enter code here
我感兴趣的控件是图片框"IDC_SENSOR"(最后一个条目),它被定义为at/as 41,62,256,76。但在屏幕上比例不同,可能是由于动态对话框布局。 所以我想知道对话框中那个框的确切位置和大小,但我无法获取这些参数。
使用的程序:
void CSerialCtrlDemoDlg::OnEventRead(char *inPacket,int inLength)
}
int x=0;
m_listboxRead.AddString(inPacket); // display the incomming data in the 'Read' listbox
CString str; // display the # of data's in 'Info'
str.Format("%d bytes read",inLength);
m_staticInfo.SetWindowText(str);
CWnd *cWndSensor = GetDlgItem(IDC_SENSOR); // show the fluctuation in the Picturebox
CDC *pDC = cWndSensor->GetDC();
for (x = 0; x < 200; x++)
{
pDC->SetPixelV(x, 10, RGB(rand() % 255, 0, 0));
}
}
for循环中的x<200应该设置为图片框的宽度。
如何获得这些尺寸?
我希望这些信息足够了(?)。
你好,CJ。
The control I'm interested in is the picturebox "IDC_SENSOR" (last entry), which is defined at/as 41,62,256,76. But on screen that proportions are different, probably due to the Dynamic Dialog Layout.
是的,没错。对话资源中的坐标在 DLU(对话单位)中指定。您可以通过调用 MapDialogRect
函数将对话框单位转换为像素。
但这并不是您想在此处执行的操作,因为您不想 hard-code 代码中的对话框坐标。如果您更改资源文件,您希望您的代码继续工作。
I want to know the exact position and size of that box whithin the dialogbox
你真正需要的是获取控件在屏幕上的坐标,以像素为单位。有两个专门为此目的设计的函数:
GetClientRect
告诉您 window 在 client 坐标中的矩形。对于一个childwindow,比如一个控件,upper-left角永远是(0, 0),lower-right角会告诉你宽度和高度。GetWindowRect
告诉您 window 在 屏幕 坐标中的矩形。这些是相对于虚拟屏幕的 "absolute" 坐标。与客户端矩形不同,window 矩形还包括 window 的 non-client 区域的大小。
您可能已经知道这一点,但此矩形的宽度是通过从其右边界减去其左边界来确定的。类似地,高度是通过从其底部边界减去其顶部边界来确定的。或者,如果您使用 MFC 包装器 class、CRect
(它在任何地方都可以与 RECT
互换,包括在调用上述两个函数时),您可以只调用 Width
和 Height
个成员函数。
至于您的特定 use-case,因为您在 child window 本身上调用 GetDC
,您将获得一个客户端 DC,并且您会需要客户坐标,所以你会调用 GetClientRect
。简单的。除了你的代码是错误的。您正在获取一个 DC 并在 WM_PAINT
消息处理程序的 外部 绘制,这意味着您绘制的任何内容都可能会以任意间隔被擦除。所有绘图都应响应 WM_PAINT
消息完成。
在您的情况下完成此操作的一种简单方法是为 IDC_SENSOR
控件设置 SS_OWNERDRAW
样式。这使 parent 对话框负责绘制控件的内容。当需要绘制 child 控件时,它会收到 WM_DRAWITEM
消息。添加一个 OnDrawItem
处理程序并在其中进行绘图。作为参数传递的 DRAWITEMSTRUCT
将告诉您应该绘制的矩形 (rcItem
)。
最后,我要注意 SetPixelV
相当慢。如果它对您来说足够快,请忽略此评论的其余部分。但是如果在屏幕上绘图真的很慢,那么你应该考虑创建并缓存位图object。绘制到该位图中,设置其各个像素,然后将该位图简单地 blit 到控件的设备上下文中。访问和操作屏幕外位图的单个像素 快得多。
我已成功使用 GetClientRect 检索到详细信息:
void CSerialCtrlDemoDlg::OnEventRead(char *inPacket,int inLength)
{
int i = 0;
int j = 0;
int x = 0;
int y = 0;
int w = 100;
int h = 50;
RECT sensorRect;
CWnd *cWndSensor = GetDlgItem(IDC_SENSOR);
CDC *pDC = cWndSensor->GetDC();
cWndSensor->GetClientRect(&sensorRect);
x = sensorRect.left;
y = sensorRect.top;
w = sensorRect.right - x;
h = sensorRect.bottom - y;
for (i = 0; i < w; i++) // this fills the picturebox (for test only)
{
for (j = 0; j < h; j++)
{
pDC->SetPixelV(i, j, RGB(rand() % 255, 0, 0));
}
}
}
所以我将此问题标记为已回答!
我不太确定这是否是 'correct' 方式,我缺乏这方面的经验。但它有效,然后它应该相当不错:)
我将按照建议使用位图,而不是(直接)绘制到图片框中。那确实更好,更容易使用。
我现在需要发现的是如何使用该位图以正确的方式更新对话框。正如科迪所说,此类绘图不应在此例程中完成,而应作为单独的 OnDrawItem 指令处理。嗯,还有很多要学。
谢谢你,CJ