Delphi中实现可以更改大小的对话框

原创 2005年04月11日 13:33:00

关键字:Dialog、对话框、resizable

1、问题的提出
问题来自Stanley_Xu,希望得到只有关闭按钮(还可以有帮助),左上也没有程序的图标并且能够更改窗口大小的对话框。
VCL中为TForm设置了BorderStyle和BorderIcons属性,用以简化窗口样式的设置(否则就要调用SetWindowLong和GetWindowLong等API函数)。TFormBorderStyle和TBorderIcon的定义和说明如下:

Value          Meaning
bsDialog       Not resizable; standard dialog box border//不能改大小
bsSingle       Not resizable; single-line border
bsNone Not     resizable; 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.

显然,通过BorderStyle和BorderIcons只能够满足一般的需要,要实现能够修改大小的对话框就有所力不能及了。
一般情况下,我要得到不能最大最小化但又可以更改大小的窗口,就把BorderStyle设置为bsSizeable,把BorderIcons的biMinimize和biMaximize去掉,结果象这样:窗口可以修改大小,但左上角有图标,:

VcLSizableDlg
图 1 带图标的对话框


注意左上角有图标。

而我们的目标则是下面的两种效果,左上角都没有图标,但窗口都可以修改大小。

OpenSaveDlg
图 2 打开文件对话框

Browse4Folder

图 3浏览文件夹对话框


2、问题解决一半
搜索了一下MSDN,找到一篇教你如何设计可以可更改大小的属性页的文章(在MFC中CPropertySheet是作为CPropertyPage子页出现的,后者从CDialog继承而来,通常不能修改大小)《
How To Design a Resizable MFC Property Sheet》,文中介绍的方法是在属性页创建之前修改窗口样式,然后手动处理WM_SIZE消息。

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;
}

我试着将同样的方法用到VCL的一个Form中。在设计时把BorderStyle设置为bsDialog,然后重载CreateParams方法。但结果是对话框确实变成了厚边框(因为有WS_THICKFRAME样式),鼠标移动到各个边框后能够自动变化,左上角也没有图标,但窗口就是不能改变大小(添加的WM_SIZE消息处理过程没有触发)。问题出在哪里呢?

HalfZoCDlgResizable

图 4 还不能完全令人满意的对话框

3、问题的解决
查了一翻Forms.pas的源代码,发现了问题所在。TCustomForm的WM_NCCREATE消息处理过程中有一个ModifySystemMenu嵌入过程,用来修改Form的系统菜单。注意下面红色文字说的是“使系统菜单看起来像对话框一样”。接下来的几句代码就把系统菜单项删得只剩下了“移动”和“关闭”。

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;

所以,问题出在由于“SC_SIZE”被删掉,窗口的样式出现了畸形:有WS_THICKFRAME(可以修改窗口大小),但不响应WM_SIZE消息(SC_SIZE被删掉)。
解决的办法很简单:实现自己的WM_NCCREATE消息处理过程,手动修改系统菜单。


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类
最终的解决方案我封装为一个继承自TForm的类,效果如下,与图1相同(如果想要图2那样的系统菜单则把调用ModifySystemMenu的行删掉),使用的时候从TZoCDlgResizable继承一个即可。
BTW:我还顺手给TZoCDlgResizable加了个SizeGrip属性,具体情况可以看代码。


图 5 没有图标、可以修改大小、带有SizeGrip的对话框

下载(exe和源代码)
http://zocsoft.vicp.net:8080/Article%20Demos/ResizableDialog.rar

5、参考资料:

MSDN: How To Design a Resizable MFC Property Sheet

引用地址:《Delphi中实现可以更改大小的对话框

Delphi如何检测Form的移动和调整尺寸事件

http://delphi.about.com/od/adptips2005/qt/formmovenotify.htm
  • MaxWoods
  • MaxWoods
  • 2014年05月19日 20:46
  • 3284

DELPHI 中 Window 消息大全使用详解

Window 消息大全使用详解 导读:    Delphi是Borland公司的一种面向对象的可视化软件开发工具。 Delphi集中了Visual C++和Visual Basic两者的优点:...
  • zang141588761
  • zang141588761
  • 2016年05月19日 08:53
  • 1805

Delphi自带的SpinEdit控件太丑了,自己写一个替换它

unit UpDownEdit; interface uses  Windows, SysUtils, Classes, Controls, StdCtrls, ComCtrls, Messages;...
  • xzhifei
  • xzhifei
  • 2004年10月28日 15:03
  • 2706

delphi 动态修改exe文件的图标

unit ICOEXE; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Form...
  • hksoobe
  • hksoobe
  • 2014年06月16日 21:44
  • 425

Delphi之面向对象的界面复用技术(继承...)

代码复用和界面复用 面向对象的编程思想强调代码的可复用。而Delphi的精髓实际上就是Object Pascal语言,Object Pascal语言是一个非常强大的面向对象的编程语言,可以通过对象的...
  • feng12301
  • feng12301
  • 2013年06月11日 17:24
  • 1572

DELPHI 中 Window 消息大全使用详解

Window 消息大全使用详解 导读:    Delphi是Borland公司的一种面向对象的可视化软件开发工具。 Delphi集中了Visual C++和Visual Basic两者的优点:...
  • zang141588761
  • zang141588761
  • 2016年05月19日 08:53
  • 1805

DELPHI 打印预览功能

在很多应用程序中,都需要程序具有打印预览功能,以避免用户由于选择不当出现打印错误。           预览实现方式为通过创建一个Tpanel的派生类并公开它的canvas属性比例尺或视区范围,使用...
  • lailai186
  • lailai186
  • 2013年05月16日 13:43
  • 1876

Delphi的“动态窗体”技术实际应用

在Delphi可视化设计环境中,允许程序员在代码编辑器中以文本的方式浏览和修改DFM文件内容。当用File/Open命令直接打开DFM文件或者选择窗体设计窗口的弹出式菜单上的View as Text命...
  • MaxWoods
  • MaxWoods
  • 2014年06月04日 23:00
  • 2054

Delphi中基本控件之SaveDialog控件的使用总结

首先向Form窗体拖一个SaveDialog控件,Name属性改为:dlgSave,然后添加一个按钮,Caption属性改为:浏览,Name属性改为:btnBrowse。 然后双击浏览按钮添加如下代码...
  • songchao_2011
  • songchao_2011
  • 2014年01月10日 22:06
  • 5658

Delphi Math里的基本函数,以及浮点数比较函数

Delphi里的好东西太多,多到让人觉得烦。这种感觉就是当年打游戏《英雄无敌3》,改了钱以后,有钱了每天都要造建筑,明明是好事,可是让人觉得烦。 先记录下来,以后再回来加强对Math单元的研究,不必...
  • Hmillet
  • Hmillet
  • 2017年02月06日 16:34
  • 661
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi中实现可以更改大小的对话框
举报原因:
原因补充:

(最多只允许输入30个字)