带控件的 DialogEx:调整大小?
DialogEx with Controls: Resizing?
目的是调整 DialogEx 的 Window 大小,使其最适合对象机器上配置的 Systemmetrics 屏幕高度和屏幕深度:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
return DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_MAIN), nullptr, DlgProc);
}
IDD_MAIN 设置为 768p 中的默认值。我们称它为 IDD_760P
,并使用其资源文件配置作为工作基础。
IDD_768P DIALOGEX 0, 0, 701, 191
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MahProject"
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
LTEXT "Add",IDC_STATIC,506,10,14,8
EDITTEXT IDC_TEXT,528,7,120,14,ES_AUTOHSCROLL
EDITTEXT IDC_NUMBER,647,7,21,14,ES_NUMBER
LTEXT "times.",IDC_STATIC,671,10,23,8
LISTBOX IDC_LIST,7,22,641,148,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP //principal, main, or chief control on form
PUSHBUTTON "&Add",IDC_ADD,650,30,46,14
PUSHBUTTON "&Up",IDC_UP,650,47,47,14
PUSHBUTTON "&Down",IDC_DOWN,650,63,47,14
PUSHBUTTON "&Sideways",IDC_CREATE,650,80,47,14
PUSHBUTTON "&UpsideDown",IDC_REMOVE,650,97,47,14
PUSHBUTTON "&Less",IDC_CLEAR,650,114,47,14
PUSHBUTTON "&More",IDC_LOGON,650,131,47,14
PUSHBUTTON "&NoMore",IDC_NOLOGON,650,148,47,14
LTEXT "Great",IDC_STATIC_ONE,530,180,70,8
CTEXT "-",IDC_SHOWCOUNT,600,180,25,8
LTEXT "Fantastic",IDC_STATIC_TWO,625,180,30,8
END
这些控件可以根据这个 post 使用 CreateWindow 单独重新校准,但是代码很多。
有没有一种子类化表单以使用额外资源的方法基于上述 IDD_1080P
、IDD_2160P,
IDD_4320P
及以后的文件预设?
我们将 GetSystemMetrics(SM_CXSCREEN)
和 GetSystemMetrics(SM_CYSCREEN)
函数放在代码的什么位置?
您可以在 WM_INITDIALOG
中调整对话框的大小
GetSystemMetrics(SM_CXSCREEN)
/GetSystemMetrics(SM_CYSCREEN)
给出全屏width/height。这将尝试与工具栏重叠。你可能不想要那个。
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktop, NULL);
将获得桌面矩形,或者您可以只显示最大化 window
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_INITDIALOG)
{
ShowWindow(hwnd, SW_MAXIMIZE);
return 0;
}
...
return FALSE;
}
您应该将对话框样式更改为以下样式:
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
请注意,您必须 move/resize 所有子控件。移动子控件时,并不是说您必须移动它在屏幕坐标中的位置,然后转换为父控件的客户端坐标 window.
例子
#include <Windows.h>
#include "resource.h"
#define rcwidth(rc) (rc.right - rc.left)
#define rcheight(rc) (rc.bottom - rc.top)
void move_resize(HWND child, int dx, int dy, int dw, int dh)
{
if (!child) return;
if (!IsWindow(child)) return;
if (!GetParent(child)) return;
//find child window's coordinates relative to top-left of parent:
RECT rc;
GetWindowRect(child, &rc);
//rc is now child control's rectangle in screen coordinates
POINT pt = { 0 };
ScreenToClient(GetParent(child), &pt);
OffsetRect(&rc, pt.x, pt.y);
//rc is now child control's rectangle relative to parent window
//prevent negative size
if ((rcwidth(rc) + dw) < 0) dw = -rcwidth(rc);
if ((rcheight(rc) + dh) < 0) dh = -rcheight(rc);
MoveWindow(child, rc.left + dx, rc.top + dy, rcwidth(rc) + dw, rcheight(rc), TRUE);
}
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static RECT save_rect;
switch (msg)
{
case WM_INITDIALOG:
GetClientRect(hwnd, &save_rect);
ShowWindow(hwnd, SW_MAXIMIZE);
break;
case WM_SIZE:
if (lParam)
{
int cx = LOWORD(lParam);
int cy = HIWORD(lParam);
int dx = cx - save_rect.right;
int dy = cy - save_rect.bottom;
//change x/y position of OK/Cancel button
move_resize(GetDlgItem(hwnd, IDCANCEL), dx, dy, 0, 0);
move_resize(GetDlgItem(hwnd, IDOK), dx, dy, 0, 0);
//change width/height of listbox:
move_resize(GetDlgItem(hwnd, IDC_LIST1), 0, 0, dx, dy);
GetClientRect(hwnd, &save_rect);
}
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK)
EndDialog(hwnd, wParam);
if (LOWORD(wParam) == IDCANCEL)
EndDialog(hwnd, wParam);
break;
}
return FALSE;
}
int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int)
{
DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, DlgProc);
return 0;
}
在 "old" 的日子里,有关于在资源文件中使用静态数据而不是当时的 "limited" 处理能力的热烈讨论。这些天似乎不是这样的问题,但是在这种情况下,处理巨大的 DPI 可能是一个问题。
我相信 Barmak 的答案是要走的路,但将其添加为权宜之计,但更便宜且不太精确的替代方案。
不幸的是,我们不能将 preprocessor 用于不同的预设,因此我们的想法是在入口点之后枚举 LPCDLGTEMPLATE 的 lpTemplate 值:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
Create_Temporary_hwnd_here
...
lpTemplate = DoSystemParametersInfoStuff(hwnd);
...
Destroy__Temporary_hwnd
...
return DialogBoxW(hInstance, MAKEINTRESOURCEW(lpTemplate), nullptr, DlgProc);
}
DoSystemParametersInfostuff returns 适当的字符串 IDD_1080P、IDD_2160P 等。如果资源文件中每个模板的 template structure 和相关数据未更改,则应该没问题。
Edit1:如果在使用 Barmak 的替代方法获得 DlgProc window 的句柄之前调用函数 MonitorFromWindow
,检查 primary/secondary 显示可能会有点尴尬。好吧,一旦离开 wWinMain 例程就不可能 return,所以下一个选择是从 wWinMain 创建另一个临时 "cheaper" hwnd 以获得适当的模板。
编辑 2:感谢 AOO,不同分辨率的表单模板都设置在可下载的电子表格中 here。
目的是调整 DialogEx 的 Window 大小,使其最适合对象机器上配置的 Systemmetrics 屏幕高度和屏幕深度:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
return DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_MAIN), nullptr, DlgProc);
}
IDD_MAIN 设置为 768p 中的默认值。我们称它为 IDD_760P
,并使用其资源文件配置作为工作基础。
IDD_768P DIALOGEX 0, 0, 701, 191
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MahProject"
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
LTEXT "Add",IDC_STATIC,506,10,14,8
EDITTEXT IDC_TEXT,528,7,120,14,ES_AUTOHSCROLL
EDITTEXT IDC_NUMBER,647,7,21,14,ES_NUMBER
LTEXT "times.",IDC_STATIC,671,10,23,8
LISTBOX IDC_LIST,7,22,641,148,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP //principal, main, or chief control on form
PUSHBUTTON "&Add",IDC_ADD,650,30,46,14
PUSHBUTTON "&Up",IDC_UP,650,47,47,14
PUSHBUTTON "&Down",IDC_DOWN,650,63,47,14
PUSHBUTTON "&Sideways",IDC_CREATE,650,80,47,14
PUSHBUTTON "&UpsideDown",IDC_REMOVE,650,97,47,14
PUSHBUTTON "&Less",IDC_CLEAR,650,114,47,14
PUSHBUTTON "&More",IDC_LOGON,650,131,47,14
PUSHBUTTON "&NoMore",IDC_NOLOGON,650,148,47,14
LTEXT "Great",IDC_STATIC_ONE,530,180,70,8
CTEXT "-",IDC_SHOWCOUNT,600,180,25,8
LTEXT "Fantastic",IDC_STATIC_TWO,625,180,30,8
END
这些控件可以根据这个 post 使用 CreateWindow 单独重新校准,但是代码很多。
有没有一种子类化表单以使用额外资源的方法基于上述 IDD_1080P
、IDD_2160P,
IDD_4320P
及以后的文件预设?
我们将 GetSystemMetrics(SM_CXSCREEN)
和 GetSystemMetrics(SM_CYSCREEN)
函数放在代码的什么位置?
您可以在 WM_INITDIALOG
GetSystemMetrics(SM_CXSCREEN)
/GetSystemMetrics(SM_CYSCREEN)
给出全屏width/height。这将尝试与工具栏重叠。你可能不想要那个。
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktop, NULL);
将获得桌面矩形,或者您可以只显示最大化 window
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_INITDIALOG)
{
ShowWindow(hwnd, SW_MAXIMIZE);
return 0;
}
...
return FALSE;
}
您应该将对话框样式更改为以下样式:
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
请注意,您必须 move/resize 所有子控件。移动子控件时,并不是说您必须移动它在屏幕坐标中的位置,然后转换为父控件的客户端坐标 window.
例子
#include <Windows.h>
#include "resource.h"
#define rcwidth(rc) (rc.right - rc.left)
#define rcheight(rc) (rc.bottom - rc.top)
void move_resize(HWND child, int dx, int dy, int dw, int dh)
{
if (!child) return;
if (!IsWindow(child)) return;
if (!GetParent(child)) return;
//find child window's coordinates relative to top-left of parent:
RECT rc;
GetWindowRect(child, &rc);
//rc is now child control's rectangle in screen coordinates
POINT pt = { 0 };
ScreenToClient(GetParent(child), &pt);
OffsetRect(&rc, pt.x, pt.y);
//rc is now child control's rectangle relative to parent window
//prevent negative size
if ((rcwidth(rc) + dw) < 0) dw = -rcwidth(rc);
if ((rcheight(rc) + dh) < 0) dh = -rcheight(rc);
MoveWindow(child, rc.left + dx, rc.top + dy, rcwidth(rc) + dw, rcheight(rc), TRUE);
}
BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static RECT save_rect;
switch (msg)
{
case WM_INITDIALOG:
GetClientRect(hwnd, &save_rect);
ShowWindow(hwnd, SW_MAXIMIZE);
break;
case WM_SIZE:
if (lParam)
{
int cx = LOWORD(lParam);
int cy = HIWORD(lParam);
int dx = cx - save_rect.right;
int dy = cy - save_rect.bottom;
//change x/y position of OK/Cancel button
move_resize(GetDlgItem(hwnd, IDCANCEL), dx, dy, 0, 0);
move_resize(GetDlgItem(hwnd, IDOK), dx, dy, 0, 0);
//change width/height of listbox:
move_resize(GetDlgItem(hwnd, IDC_LIST1), 0, 0, dx, dy);
GetClientRect(hwnd, &save_rect);
}
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK)
EndDialog(hwnd, wParam);
if (LOWORD(wParam) == IDCANCEL)
EndDialog(hwnd, wParam);
break;
}
return FALSE;
}
int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int)
{
DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, DlgProc);
return 0;
}
在 "old" 的日子里,有关于在资源文件中使用静态数据而不是当时的 "limited" 处理能力的热烈讨论。这些天似乎不是这样的问题,但是在这种情况下,处理巨大的 DPI 可能是一个问题。
我相信 Barmak 的答案是要走的路,但将其添加为权宜之计,但更便宜且不太精确的替代方案。
不幸的是,我们不能将 preprocessor 用于不同的预设,因此我们的想法是在入口点之后枚举 LPCDLGTEMPLATE 的 lpTemplate 值:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
Create_Temporary_hwnd_here
...
lpTemplate = DoSystemParametersInfoStuff(hwnd);
...
Destroy__Temporary_hwnd
...
return DialogBoxW(hInstance, MAKEINTRESOURCEW(lpTemplate), nullptr, DlgProc);
}
DoSystemParametersInfostuff returns 适当的字符串 IDD_1080P、IDD_2160P 等。如果资源文件中每个模板的 template structure 和相关数据未更改,则应该没问题。
Edit1:如果在使用 Barmak 的替代方法获得 DlgProc window 的句柄之前调用函数 MonitorFromWindow
,检查 primary/secondary 显示可能会有点尴尬。好吧,一旦离开 wWinMain 例程就不可能 return,所以下一个选择是从 wWinMain 创建另一个临时 "cheaper" hwnd 以获得适当的模板。
编辑 2:感谢 AOO,不同分辨率的表单模板都设置在可下载的电子表格中 here。