Introduction
One common user frustration is dialog boxes that cannot be resized. Although there are many approaches to solve this problem, most are oriented towards new code that can be designed around resizing. This leaves an existing body of dialogs that can never be resized. This article describes an approach that allows resizing to be easily retrofitted to Win32 dialog boxes. By dialog boxes, note that this specifically refers to dialog boxes defined in resource scripts that are instantiated with the DialogBox()
(or similar) Win32 function.
Background
Traditional Win32 dialogs are defined in resource scripts that define the existence and placement of controls within the dialog. A typical approach to implementing resizing, however, involves writing code to alter the placement defined by the resource script. Following contemporary programming practice, this article instead suggests using the resource script to define resize conventions, leaving the code itself minimally altered. The main goal is minimal change to existing code, making a retrofit as simple as possible.
Resizing can create many pitfalls for developers and designers. Dialogs must be tested not just to work well in a preconfigured size, but in every potential size. To minimize the test requirement, the approach suggested by this article involves designing a dialog in its minimal size, creating the dialog at this minimal size, but the user retains the option to enlarge the dialog. New space created by this resize operation is then credited to controls following markup in the resource template.
In addition, constraints can be optionally imposed on the resize operation. This may prevent a dialog being limitlessly expanded, or could prevent a dialog being resized in a particular orientation (for example, the dialog can be resized vertically but not horizontally.)
Using the Code
#include "ResizeDialog.h"
into the resource script that contains the dialog box definitions.
- From within the dialog's
DialogProc()
function, call the provided resizer function ResizeDialogProc()
. This will process messages related to resizing from the markup.
- Before instantiating any resizeable dialog, the custom controls used by the template must be registered. This is done by calling
ResizeDialogInitialize()
when the application is initialized.
- Alter the dialog style (in the resource script) to support resizing. For example, change the dialog's style from:
STYLE WS_POPUPWINDOW | WS_CAPTION
to:
STYLE WS_OVERLAPPED | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU
- Define how to allocate new space to individual controls on the dialog by adding the
DIALOGRESIZECONTROL
helper markup before each control. This markup includes four integer values between 0 and 100, defining the percentage of new space to allocate to shifting the object left, shifting it down, making it wider, and making it higher respectively. For example, if an object should grow in proportion to the size of the dialog being resized, the values would be { 0, 0, 100, 100 }. An object top-left aligned, and not growing in size, would be { 0, 0, 0, 0 }. An object bottom-right aligned, and not growing in size, would be { 100, 100, 0, 0 }.
- If constraints should be imposed on how the dialog is resized, add a single
DIALOGRESIZE
helper markup at the beginning of the dialog. This markup includes two integer values that should be either 0, indicating no limit, or 100 and greater, indicating the maximum percentage that the dialog can grow from its original size. For example, to define a dialog that can grow infinitely horizontally, but only by 25% vertically, use { 0, 125 }.
To provide an illustration as to the changes required, consider the following non-resizable dialog resource.
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
}
This dialog defines two lists, separated vertically, and a push button at the bottom of the dialog. When changed to allocate new space to these lists, and to reposition the second list given the new space allocated to the first, the resizeable dialog resource looks like:
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
}
In addition, the dialog callback function must be minimally altered to process the resize. The pResizeState
value below is memory allocated by, and maintained by, the resize dialog module recording the initial state of controls and how they should be processed.
PVOID pResizeState = NULL;
BOOL CALLBACK
TestDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
ResizeDialogProc( hDlg, uMsg, wParam, lParam, &pResizeState );
...
Finally, the module must be initialized prior to invoking the dialog box:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
ResizeDialogInitialize( hInst );
DialogBox( hInst, MAKEINTRESOURCE(TESTDIALOG4B), NULL, TestDialogProc );
...
Points of Interest
In order to implement the markup, extended data is recorded inside of custom controls. The format for this data has been defined for a long time, however:
- Windows 95 and its successors have no support for this extended information, so the approach presented here will not work on those systems and is limited to Windows NT-based systems. The code attempts to detect this condition and prevent resizing when it cannot be supported.
- Although Microsoft's resource compiler supports this syntax, other resource compilers (notably GNU's windres) do not.
- This syntax is only available on
DIALOGEX
resources. It will generate resource compilation errors on DIALOG
resources. Be sure to convert any DIALOG
resources to DIALOGEX
before attempting to use this code.
The code works by processing WM_SIZE
messages sent to the dialog box, and enumerating all controls on the dialog to find instances of the dialog resize helper control. Each helper control is sent a WM_RESIZEPARENT
message which indicates it needs to resize. The helper control finds the immediate next control on the dialog (the "buddy" control) and performs appropriate transformation of it as specified by the data attached to the resize helper control.
Since all values are percentages, the code also must capture the initial size of the dialog, as well as the initial size of each control that will be resized. These values are captured when the dialog is created, and when the first WM_SIZE
is processed, respectively. These values are then computed against the percentage values in order to produce the final size for a control when the dialog is resized.
History
- 2 Jan 2011 - Initial creation