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

关键字: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中实现可以更改大小的对话框

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Delphi,可以使用TColorDialog组件实现颜色选择对话框。如果想要自定义颜色选择对话框,可以继承TColorDialog并重写其的一些方法。 下面是一个简单的示例代码,演示如何实现自定义颜色选择对话框: ```delphi unit CustomColorDialog; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls; type TCustomColorDialog = class(TColorDialog) private FCustomColors: array[0..15] of TColor; protected procedure DoShow; override; procedure PaintRect(Canvas: TCanvas; Rect: TRect; Color: TColor); virtual; public constructor Create(AOwner: TComponent); override; published property CustomColors: array[0..15] of TColor read FCustomColors write FCustomColors; end; procedure Register; implementation {$R *.dfm} constructor TCustomColorDialog.Create(AOwner: TComponent); begin inherited; FCustomColors[0] := clBlack; FCustomColors[1] := clMaroon; FCustomColors[2] := clGreen; FCustomColors[3] := clOlive; FCustomColors[4] := clNavy; FCustomColors[5] := clPurple; FCustomColors[6] := clTeal; FCustomColors[7] := clGray; FCustomColors[8] := clSilver; FCustomColors[9] := clRed; FCustomColors[10] := clLime; FCustomColors[11] := clYellow; FCustomColors[12] := clBlue; FCustomColors[13] := clFuchsia; FCustomColors[14] := clAqua; FCustomColors[15] := clWhite; end; procedure TCustomColorDialog.DoShow; var I: Integer; BtnRect, ColorRect: TRect; BtnWidth, BtnHeight, ColorWidth, ColorHeight: Integer; BtnTop, ColorTop, ColorLeft: Integer; BtnCaption: string; BtnFont: TFont; BtnColor: TColor; Canvas: TCanvas; begin inherited; Canvas := TCanvas.Create; try Canvas.Handle := GetDC(Handle); BtnWidth := 50; BtnHeight := 20; ColorWidth := 15; ColorHeight := 15; BtnTop := ClientHeight - BtnHeight - 10; ColorTop := 10; ColorLeft := ClientWidth - 20 - (4 * ColorWidth); BtnFont := TFont.Create; try BtnFont.Assign(Font); BtnFont.Style := [fsBold]; for I := 0 to 3 do begin BtnRect := Rect(20 + (I * BtnWidth), BtnTop, 20 + ((I + 1) * BtnWidth), BtnTop + BtnHeight); BtnColor := CustomColors[I]; PaintRect(Canvas, BtnRect, BtnColor); BtnCaption := 'Color ' + IntToStr(I + 1); Canvas.Font := BtnFont; Canvas.Brush.Style := bsClear; DrawText(Canvas.Handle, PChar(BtnCaption), -1, BtnRect, DT_SINGLELINE or DT_CENTER or DT_VCENTER); for J := 0 to 3 do begin ColorRect := Rect(ColorLeft + (J * ColorWidth), ColorTop + (I * ColorHeight), ColorLeft + ((J + 1) * ColorWidth), ColorTop + ((I + 1) * ColorHeight)); PaintRect(Canvas, ColorRect, CustomColors[(I * 4) + J]); end; end; finally BtnFont.Free; end; finally ReleaseDC(Handle, Canvas.Handle); Canvas.Free; end; end; procedure TCustomColorDialog.PaintRect(Canvas: TCanvas; Rect: TRect; Color: TColor); begin Canvas.Brush.Color := Color; Canvas.Pen.Color := Color; Canvas.Rectangle(Rect); end; procedure Register; begin RegisterComponents('Samples', [TCustomColorDialog]); end; end. ``` 以上代码,我们创建了一个名为TCustomColorDialog的自定义颜色选择对话框。在DoShow方法,我们使用TCanvas绘制了自定义颜色区域和按钮区域。在PaintRect方法,我们绘制了矩形,并填充了指定的颜色。 要使用自定义颜色选择对话框,只需将TCustomColorDialog组件放置在窗体,并将其Execute方法调用即可:CustomColorDialog1.Execute。 希望这个示例对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值