Keywords : Dialog, dialog, resizable
1. Problem raised
The problem comes from Stanley_Xu, hoping to get only the close button (it can also help), there is no icon for the program on the top left and the dialog box is able to change the window size.
The BorderStyle and BorderIcons properties are set for TForm in VCL to simplify the setting of window styles (otherwise, API functions such as SetWindowLong and GetWindowLong must be called). The definition and description of TFormBorderStyle and TBorderIcon are as follows:
Value Meaning
bsDialog Not resizable; standard dialog box border//cannot change size
bsSingle Not resizable; single-line border
bsNoneNotresizable; no visible border line
bsSizeable Standard resizable border
bsToolWindow like bsSingle but with a smaller caption
bsSizeToolWin like bsSizeable with a smaller caption
type TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);
TBorderIcons = set of TBorderIcon;
Value Meaning
biSystemMenu The form has a Control menu (also known as a System menu).
biMinimize The form has a Minimize button
biMaximize The form has a Maximize button
biHelp If BorderStyle is bsDialog or biMinimize and biMaximize are excluded, a question mark appears in the form's title bar and when clicked, the cursor changes to crHelp; otherwise,no question mark appears.
Obviously, BorderStyle and BorderIcons can only meet general needs, and it is impossible to implement dialog boxes that can modify sizes.
Generally speaking, if I want to get a window that cannot be maximized but can change the size, I set BorderStyle to bsSizeable, remove the biMinimize and biMaximize of BorderIcons, and the result is like this: the window can be modified, but there is an icon in the upper left corner,:
Figure 1 Dialog box with icon
Note that there is an icon in the upper left corner.
Our goal is the two effects below. There is no icon in the upper left corner, but the window can be changed in size.
Figure 2 Open File dialog box
Figure 3 Browse folder dialog box
2. Half the problem is solved
I searched for MSDN and found an article that teaches you how to design property pages that can change size (in MFC, CPRopertySheet appears as a CPropertyPage subpage, which inherits from CDialog and cannot usually modify the size) "How To Design a Resizable MFC Property Sheet, the method introduced in the article is to modify the window style before the property page is created, and then manually process the WM_SIZE message.
int CALLBACK CMyPropertySheet::XmnPropSheetCallback(HWND hWnd, UINT message, LPARAM lParam)
{
extern int CALLBACK AfxPropSheetCallback(HWND, UINT message, LPARAM lParam);
// XMN: Call MFC's callback
int nRes = AfxPropSheetCallback(hWnd, message, lParam);
switch (message)
{
case PSCB_PRECREATE:
// Set our own window styles
((LPDLGTEMPLATE)lParam)->style |= (DS_3DLOOK | DS_SETFONT
| WS_THICKFRAME | WS_SYSMENU | WS_POPUP | WS_VISIBLE | WS_CAPTION);
break;
}
return nRes;
}
I've tried using the same method into a Form of VCL. Set BorderStyle to bsDialog when designing, and then overload the CreateParams method. But the result is that the dialog box does become a thick border (because it has WS_THICKFRAME style), the mouse can automatically change after moving to each border, and there is no icon in the upper left corner, but the window cannot change its size (the added WM_SIZE message processing process is not triggered) . What's the problem?
Figure 4 Dialog Box that is not yet completely satisfactory
3. Solve the problem
I checked the source code of Forms.pas and found the problem. There is a ModifySystemMenu embedding process in the WM_NCCREATE message processing process of TCustomForm, which is used to modify the system menu of Form. Note that the red text below says "make the system menu look like a dialog box". The next few sentences of code deleted the system menu items to the point where only "move" and "close".
procedure TCustomForm.WMNCCreate(var Message: TWMNCCreate);
procedure ModifySystemMenu;
var
SysMenu: HMENU;
Begin
...
{ Modify the system menu to look more like it's s'pose to }
SysMenu := GetSystemMenu(Handle, False);
if FBorderStyle = bsDialog then
Begin
{ Make the system menu look like a dialog which has only
Move and Close }
DeleteMenu(SysMenu, SC_TASKLIST, MF_BYCOMMAND);
DeleteMenu(SysMenu, 7, MF_BYPOSITION);
DeleteMenu(SysMenu, 5, MF_BYPOSITION);
DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_RESTORE, MF_BYCOMMAND);
end else
...
end;
Begin
inherited;
SetMenu(FMenu);
if not (csDesigning in ComponentState) then ModifySystemMenu;
end;
Therefore, the problem is that because "SC_SIZE" is deleted, the window style has a deformity: there is WS_THICKFRAME (the window size can be modified), but it does not respond to the WM_SIZE message (SC_SIZE is deleted).
The solution is very simple: implement your own WM_NCCREATE message processing process and manually modify the system menu.
procedure TZoCDlgResizable.WMNCCreate(var Message: TWMNCCreate);
//The following codes are copied from Form.pas line 4047, Delphi 7 sp1.
procedure ModifySystemMenu;
var
SysMenu: HMENU;
Begin
SysMenu := GetSystemMenu(Handle, False);
{ Make the system menu look like a dialog which has only
Move, Size and Close commands}
DeleteMenu(SysMenu, SC_TASKLIST, MF_BYCOMMAND);
DeleteMenu(SysMenu, 7, MF_BYPOSITION);
//Don't remove the separater before CLOSE command.
// DeleteMenu(SysMenu, 5, MF_BYPOSITION);
DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND);
{ Don't remove the SIZE command, otherwise we'll lose the
capability of resizing the Dialog. }
// DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND);
DeleteMenu(SysMenu, SC_RESTORE, MF_BYCOMMAND);
end;
Begin
{ Skip TCustomForm's WM_NCCREATE handler, which remove
the SIZE command from the System Menu.}
inherited DefaultHandler(Message);
//Dealing with the System Menu in our own way.
ModifySystemMenu;
end;
4. TZoCDlgResizable class
The final solution I encapsulate as a class inherited from TForm. The effect is as follows, the same as Figure 1 (if you want the system menu like Figure 2, delete the line calling ModifySystemMenu). When using it, you can inherit one from TZoCDlgResizable. .
BTW: I also added a SizeGrip attribute to TZoCDlgResizable. For the specific situation, you can see the code.
Figure 5 Dialog box with SizeGrip without icon
Download (exe and source code)
http://www.zocsoft.com/temp/Resizable_Dialog.rar
5. Reference materials:
MSDN: How To Design a Resizable MFC Property Sheet
Quote address: "Implementing a dialog box that can change the size in Delphi"