使用 DialogBox() 和对话框资源调整 Win32 对话框大小
演示如何通过资源标记,以最小的更改来调整对话框上的控件大小,同时保持现有的 Win32 代码不变
引言
用户普遍感到沮丧的一个问题是对话框无法调整大小。尽管有许多方法可以解决此问题,但大多数方法都侧重于可以围绕调整大小进行设计的代码。这使得现有的对话框无法调整大小。本文介绍了一种允许轻松地将调整大小功能重新应用到 Win32 对话框的方法。请注意,此处所说的对话框特指在资源脚本中定义、使用 DialogBox()
(或类似)Win32 函数实例化的对话框。
背景
传统的 Win32 对话框在资源脚本中定义,用于定义对话框中控件的存在和位置。然而,实现调整大小的典型方法是编写代码来修改资源脚本定义的布局。遵循当代的编程实践,本文建议使用资源脚本来定义调整大小约定,尽量少修改代码本身。主要目标是对现有代码进行最小的更改,使改造尽可能简单。
调整大小可能给开发人员和设计师带来许多潜在问题。对话框不仅要在预设大小下运行良好,还必须在所有可能的大小下进行测试。为了尽量减少测试需求,本文提出的方法涉及将对话框设计为其最小尺寸,并在此最小尺寸下创建对话框,但用户保留放大对话框的选项。此调整大小操作产生的新空间将根据资源模板中的标记分配给控件。
此外,还可以选择性地对调整大小操作施加约束。这可以防止对话框无限放大,或者防止对话框在特定方向上被调整大小(例如,对话框可以垂直调整大小,但不能水平调整大小)。
Using the Code
- 将
#include "ResizeDialog.h"
包含到包含对话框定义所在的资源脚本中。 - 在对话框的
DialogProc()
函数内部,调用提供的 resizer 函数ResizeDialogProc()
。这将处理标记中与调整大小相关的消息。 - 在实例化任何可调整大小的对话框之前,必须注册模板使用的自定义控件。这是通过在应用程序初始化时调用
ResizeDialogInitialize()
来完成的。 - 修改对话框样式(在资源脚本中)以支持调整大小。例如,将对话框的样式从
STYLE WS_POPUPWINDOW | WS_CAPTION
to
STYLE WS_OVERLAPPED | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU
- 通过在每个控件之前添加
DIALOGRESIZECONTROL
辅助标记,来定义如何将新空间分配给对话框中的各个控件。此标记包含四个介于 0 到 100 之间的整数值,分别定义了用于将对象向左移动、向下移动、使其变宽和使其变高的百分比。例如,如果一个对象应该与其调整大小的对话框大小成比例增长,则值为 { 0, 0, 100, 100 }。一个顶部左对齐且大小不变的对象将是 { 0, 0, 0, 0 }。一个底部右对齐且大小不变的对象将是 { 100, 100, 0, 0 }。 - 如果需要对对话框的调整大小方式施加约束,请在对话框的开头添加一个单独的
DIALOGRESIZE
辅助标记。此标记包含两个整数值,可以是 0(表示无限制),或者大于等于 100(表示对话框可以从原始尺寸增大的最大百分比)。例如,要定义一个可以水平无限增长,但垂直只能增长 25% 的对话框,请使用 { 0, 125 }。
为了说明所需的更改,请考虑以下不可调整大小的对话框资源。
TESTDIALOG4A DIALOGEX 10, 10, 140, 165
STYLE WS_POPUPWINDOW | WS_CAPTION
FONT 8, "MS Shell Dlg"
CAPTION "TestDialog4"
{
LTEXT "Description of &first list:", 1, 5, 5, 130, 10
LISTBOX 2, 5, 15, 130, 60, WS_TABSTOP
LTEXT "Description of &second list:", 3, 5, 75, 130, 10
LISTBOX 4, 5, 85, 130, 60, WS_TABSTOP
DEFPUSHBUTTON "&Close", 5, 5, 145, 60, 15, WS_TABSTOP
}
此对话框定义了两个列表,垂直分隔,并在对话框底部有一个按钮。当更改为将新空间分配给这些列表,并根据分配给第一个列表的新空间来重新定位第二个列表时,可调整大小的对话框资源看起来像
TESTDIALOG4B DIALOGEX 10, 10, 140, 165
STYLE WS_OVERLAPPED | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Shell Dlg"
CAPTION "TestDialog4"
{
LTEXT "Description of &first list:", 1, 5, 5, 130, 10
DIALOGRESIZECONTROL { 0, 0, 100, 50 }
LISTBOX 2, 5, 15, 130, 60, WS_TABSTOP
DIALOGRESIZECONTROL { 0, 50, 0, 0 }
LTEXT "Description of &second list:", 3, 5, 75, 130, 10
DIALOGRESIZECONTROL { 0, 50, 100, 50 }
LISTBOX 4, 5, 85, 130, 60, WS_TABSTOP
DIALOGRESIZECONTROL { 0, 100, 0, 0 }
DEFPUSHBUTTON "&Close", 5, 5, 145, 60, 15, WS_TABSTOP
}
此外,必须对对话框回调函数进行最小的修改以处理调整大小。下面的 pResizeState
值是 resizer 对话框模块分配和维护的内存,用于记录控件的初始状态以及如何处理它们。
PVOID pResizeState = NULL;
BOOL CALLBACK
TestDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
ResizeDialogProc( hDlg, uMsg, wParam, lParam, &pResizeState );
...
最后,模块必须在调用对话框之前进行初始化
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
ResizeDialogInitialize( hInst );
DialogBox( hInst, MAKEINTRESOURCE(TESTDIALOG4B), NULL, TestDialogProc );
...
关注点
为了实现标记,扩展数据被记录在自定义控件内部。此数据的格式已经定义了很长时间,但是
- Windows 95 及其后续版本不支持此扩展信息,因此本文提出的方法在这些系统上将无法工作,并且仅限于基于 Windows NT 的系统。代码会尝试检测此情况并阻止在不支持的情况下进行调整大小。
- 尽管 Microsoft 的资源编译器支持此语法,但其他资源编译器(特别是 GNU 的 windres)不支持。
- 此语法仅在
DIALOGEX
资源上可用。它会在DIALOG
资源上生成资源编译错误。在尝试使用此代码之前,请务必将任何DIALOG
资源转换为DIALOGEX
。
该代码通过处理发送到对话框的 WM_SIZE
消息,并枚举对话框上的所有控件来查找对话框调整大小辅助控件的实例。每个辅助控件都会收到一个 WM_RESIZEPARENT
消息,指示它需要调整大小。辅助控件会找到对话框上的紧邻的下一个控件(“伙伴”控件),并根据附加到调整大小辅助控件的数据对其进行适当的转换。
由于所有值都是百分比,因此代码还必须捕获对话框的初始大小,以及将要调整大小的每个控件的初始大小。这些值分别在创建对话框时和处理第一个 WM_SIZE
消息时捕获。然后将这些值与百分比值进行计算,以生成对话框调整大小时控件的最终大小。
历史
- 2011 年 1 月 2 日 - 初始创建