在软件开发中,用户界面的灵活性和适应性是至关重要的。随着屏幕分辨率的提高和用户需求的多样化,能够动态调整大小的对话框变得越来越重要。尽管有许多方法可以解决这个问题,但大多数方法都是针对可以重新设计的代码。对于已经存在的对话框,这些方法往往不适用。本文将介绍一种技术,使得Win32对话框可以轻松地进行尺寸调整,而不需要对现有代码进行大量修改。
传统的Win32对话框是在资源脚本中定义的,这些脚本确定了对话框中控件的存在和位置。然而,实现尺寸调整通常需要编写代码来改变资源脚本中定义的位置。本文建议使用资源脚本来定义尺寸调整的规则,从而最小化代码本身的修改。主要目标是对现有代码的改动尽可能小,使得尺寸调整的改造尽可能简单。
尺寸调整可能会给开发者和设计师带来许多问题。对话框不仅要在预设的尺寸下工作良好,还要在所有可能的尺寸下都能正常工作。为了最小化测试需求,本文建议的方法是设计一个最小尺寸的对话框,创建对话框时使用这个最小尺寸,但用户可以选择放大对话框。通过尺寸调整操作创建的新空间将根据资源模板中的标记分配给控件。
此外,还可以选择性地对尺寸调整操作施加限制。这可以防止对话框无限制地扩大,或者可以防止对话框在特定方向上进行尺寸调整(例如,对话框可以垂直调整大小,但不能水平调整)。
首先,需要将头文件 "ResizeDialog.h" 包含到包含对话框定义的资源脚本中。
#include "ResizeDialog.h"
在对话框的 DialogProc() 函数中,调用提供的 resizer 函数 ResizeDialogProc()。这将处理与尺寸调整相关的标记消息。
BOOL CALLBACK TestDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
ResizeDialogProc(hDlg, uMsg, wParam, lParam, &pResizeState);
...
}
在实例化任何可调整大小的对话框之前,必须注册模板中使用的自定义控件。这可以通过在应用程序初始化时调用 ResizeDialogInitialize() 来完成。
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) {
ResizeDialogInitialize(hInst);
...
}
需要修改对话框的风格(在资源脚本中)以支持尺寸调整。例如,将对话框的风格从:
STYLE WS_POPUPWINDOW | WS_CAPTION
更改为:
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 值是由尺寸调整对话框模块分配并维护的内存,记录了控件的初始状态以及它们应该如何被处理。
PVOID pResizeState = NULL;
最后,模块必须在调用对话框之前进行初始化:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) {
ResizeDialogInitialize(hInst);
...
}
为了实现标记,扩展数据被记录在自定义控件中。这种数据的格式已经定义了很长时间,但是:
代码通过处理发送到对话框的WM_SIZE消息,并枚举对话框上的所有控件以找到对话框尺寸调整辅助控件的实例来工作。每个辅助控件会收到一个WM_RESIZEPARENT消息,指示它需要调整大小。辅助控件找到对话框上的下一个控件("buddy"控件),并根据附加到尺寸调整辅助控件的数据对其进行适当的转换。