• Delphi 对话框实现源码分析


    Delphi 对话框实现源码分析

     

     

    简介

    在这篇文章中,我将大概的从Delphi XE2 的Dialogs单元入手,分析ShowMessage,MessageBox等对话框运行原理,希望能帮助你理解Delphi,不求你爱上她,只求让你能快速地解决问题。

    跟踪代码

    为了了解这些对话框的运行原理,我们需要跟踪进源代码中去,为此,你需要做如下设置

    1. 简单创建一个使用了ShowMessage的VCL应用程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    type
      TForm1 = class(TForm)
        Edit1: TEdit;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowMessage(Edit1.Text);
      MessageBox(Self.Handle,PChar(Edit1.Text),PChar(Application.Title),
        MB_ICONINFORMATION or MB_OK);
      MessageDlg(Edit1.Text,mtInformation,[mbOK,mbCancel],0);
    end;
     
    end.

    2. 在29行里设置一个断点, 再在Edit里输入一些内容,按下Message Box按钮, 按F7跟踪到Dialogs单元, 经过一段时间的仔细跟踪, 你会发现程序运行到下面一段代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function MessageDlgPosHelp(const Msg: string; DlgType: TMsgDlgType;
      Buttons: TMsgDlgButtons; HelpCtx: Longint; X, Y: Integer;
      const HelpFileName: string): Integer;
    begin
      if (Win32MajorVersion >= 6) and UseLatestCommonDialogs and ThemeServices.ThemesEnabled then
        Result := DoTaskMessageDlgPosHelp('', Msg, DlgType, Buttons,
          HelpCtx, X, Y, HelpFileName)
      else
        Result := DoMessageDlgPosHelp(CreateMessageDialog(Msg, DlgType, Buttons),
          HelpCtx, X, Y, HelpFileName);
    end;

    函数MessageDlgPosHelp指出, 如果当前系统是Vista,sever2008或以上版本的系统,那就调用DoTaskMessageDlgPosHelp函数进行对话框显示, 否则调用DoMessageDlgPosHelp显示对话框. 继续跟踪DoTaskMessageDlgPosHelp函数, 你会发现如下一段代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    function TCustomTaskDialog.DoExecute(ParentWnd: HWND): Boolean;
    const
      CTaskDlgFlags: array[TTaskDialogFlag] of Cardinal = (
        TDF_Enable_Hyperlinks, TDF_Use_Hicon_Main,
        tdf_Use_Hicon_Footer, TDF_ALLOW_DIALOG_CANCELLATION,
        TDF_USE_COMMAND_LINKS, TDF_USE_COMMAND_LINKS_NO_ICON,
        TDF_EXPAND_FOOTER_AREA, TDF_EXPANDED_BY_DEFAULT,
        TDF_VERIFICATION_FLAG_CHECKED, TDF_SHOW_PROGRESS_BAR,
        TDF_SHOW_MARQUEE_PROGRESS_BAR, TDF_CALLBACK_TIMER,
        TDF_POSITION_RELATIVE_TO_WINDOW, TDF_RTL_LAYOUT,
        TDF_NO_DEFAULT_RADIO_BUTTON, TDF_CAN_BE_MINIMIZED);
     
      CTaskDlgCommonButtons: array[TTaskDialogCommonButton] of Cardinal = (
        TDCBF_OK_BUTTON, TDCBF_YES_BUTTON, TDCBF_NO_BUTTON,
        TDCBF_CANCEL_BUTTON, TDCBF_RETRY_BUTTON, TDCBF_CLOSE_BUTTON);
     
      CTaskDlgDefaultButtons: array[TTaskDialogCommonButton] of Integer = (
        IDOK, IDYES, IDNO, IDCANCEL, IDRETRY, IDCLOSE);
     
    var
      LWindowList: TTaskWindowList;
      LModalResult: Integer;
      LRadioButton: Integer;
      LFlag: TTaskDialogFlag;
      LFocusState: TFocusState;
      LVerificationChecked: LongBool;
      LTaskDialog: TTaskDialogConfig;
      LCommonButton: TTaskDialogCommonButton;
    begin
      if Win32MajorVersion < 6 then
        raise EPlatformVersionException.CreateResFmt({$IFNDEF CLR}@{$ENDIF}SWindowsVistaRequired, [ClassName]);
      if not ThemeServices.ThemesEnabled then
        raise Exception.CreateResFmt({$IFNDEF CLR}@{$ENDIF}SXPThemesRequired, [ClassName]);
     
    {$IF NOT DEFINED(CLR)}
      FillChar(LTaskDialog, SizeOf(LTaskDialog), 0);
    {$IFEND}
      with LTaskDialog do
      begin
        // Set Size, Parent window, Flags
        cbSize := SizeOf(LTaskDialog);
        hwndParent := ParentWnd;
        dwFlags := 0;
        for LFlag := Low(TTaskDialogFlag) to High(TTaskDialogFlag) do
          if LFlag in FFlags then
            dwFlags := dwFlags or CTaskDlgFlags[LFlag];
     
        // Set CommonButtons
        dwCommonButtons := 0;
        for LCommonButton := Low(TTaskDialogCommonButton) to High(TTaskDialogCommonButton) do
          if LCommonButton in FCommonButtons then
            dwCommonButtons := dwCommonButtons or CTaskDlgCommonButtons[LCommonButton];
     
        // Set Content, MainInstruction, Title, MainIcon, DefaultButton
        if FText <> '' then
          pszContent := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FText));
        if FTitle <> '' then
          pszMainInstruction := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FTitle));
        if FCaption <> '' then
          pszWindowTitle := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FCaption));
        if tfUseHiconMain in FFlags then
          hMainIcon := FCustomMainIcon.Handle
        else
        begin
          if FMainIcon in [tdiNone..tdiShield] then
            pszMainIcon := LPCWSTR(CTaskDlgIcons[FMainIcon])
          else
            pszMainIcon := LPCWSTR(MakeIntResourceW(Word(FMainIcon)));
        end;
        nDefaultButton := CTaskDlgDefaultButtons[FDefaultButton];
     
        // Set Footer, FooterIcon
        if FFooterText <> '' then
          pszFooter := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FFooterText));
        if tfUseHiconFooter in FFlags then
          hFooterIcon := FCustomFooterIcon.Handle
        else
        begin
          if FFooterIcon in [tdiNone..tdiShield] then
            pszFooterIcon := LPCWSTR(CTaskDlgIcons[FFooterIcon])
          else
            pszFooterIcon := LPCWSTR(MakeIntResourceW(Word(FFooterIcon)));
        end;
     
        // Set VerificationText, ExpandedInformation, CollapsedControlText
        if FVerificationText <> '' then
          pszVerificationText := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FVerificationText));
        if FExpandedText <> '' then
          pszExpandedInformation := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FExpandedText));
        if FExpandButtonCaption <> '' then
          pszCollapsedControlText := {$IFNDEF CLR}PWideChar{$ENDIF}(WideString(FExpandButtonCaption));
     
        // Set Buttons
        cButtons := FButtons.Count;
        if cButtons > 0 then
          pButtons := FButtons.Buttons;
        if FButtons.DefaultButton <> nil then
          nDefaultButton := FButtons.DefaultButton.ModalResult;
     
        // Set RadioButtons
        cRadioButtons := FRadioButtons.Count;
        if cRadioButtons > 0 then
          pRadioButtons := FRadioButtons.Buttons;
        if not (tfNoDefaultRadioButton in FFlags) and (FRadioButtons.DefaultButton <> nil) then
          nDefaultRadioButton := FRadioButtons.DefaultButton.ModalResult;
     
        // Prepare callback
    {$IF DEFINED(CLR)}
        pfCallBack := @CallbackProc;
    {$ELSE}
        lpCallbackData := LONG_PTR(Self);
        pfCallback := @TaskDialogCallbackProc;
    {$IFEND}
      end;
     
      LWindowList := DisableTaskWindows(ParentWnd);
      LFocusState := SaveFocusState;
      try
        Result := TaskDialogIndirect(LTaskDialog, {$IFNDEF CLR}@{$ENDIF}LModalResult,
          {$IFNDEF CLR}@{$ENDIF}LRadioButton, {$IFNDEF CLR}@{$ENDIF}LVerificationChecked) = S_OK;
        FModalResult := LModalResult;
        if Result then
        begin
          FButton := TTaskDialogButtonItem(FButtons.FindButton(FModalResult));
          FRadioButton := TTaskDialogRadioButtonItem(FRadioButtons.FindButton(LRadioButton));
          if LVerificationChecked then
            Include(FFlags, tfVerificationFlagChecked)
          else
            Exclude(FFlags, tfVerificationFlagChecked);
        end;
      finally
        EnableTaskWindows(LWindowList);
        SetActiveWindow(ParentWnd);
        RestoreFocusState(LFocusState);
      end;
    end;

    上面这段代码在Dialogs单元的第5407行, 该函数先进行可用性判断, 然后填充 

    1
    LTaskDialog: TTaskDialogConfig;


    一个TTaskDialogConfig的结构体, 该结构体定义在CommCtrl单元第9550行, 其定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    type
      { $EXTERNALSYM TASKDIALOGCONFIG}
      TASKDIALOGCONFIG = packed record
        cbSize: UINT;
        hwndParent: HWND;
        hInstance: HINST;                     // used for MAKEINTRESOURCE() strings
        dwFlags: DWORD;                       // TASKDIALOG_FLAGS (TDF_XXX) flags
        dwCommonButtons: DWORD;               // TASKDIALOG_COMMON_BUTTON (TDCBF_XXX) flags
        pszWindowTitle: LPCWSTR;              // string or MAKEINTRESOURCE()
        case Integer of
          0: (hMainIcon: HICON);
          1: (pszMainIcon: LPCWSTR;
              pszMainInstruction: LPCWSTR;
              pszContent: LPCWSTR;
              cButtons: UINT;
              pButtons: PTaskDialogButton;
              nDefaultButton: Integer;
              cRadioButtons: UINT;
              pRadioButtons: PTaskDialogButton;
              nDefaultRadioButton: Integer;
              pszVerificationText: LPCWSTR;
              pszExpandedInformation: LPCWSTR;
              pszExpandedControlText: LPCWSTR;
              pszCollapsedControlText: LPCWSTR;
              case Integer of
                0: (hFooterIcon: HICON);
                1: (pszFooterIcon: LPCWSTR;
                    pszFooter: LPCWSTR;
                    pfCallback: TFTaskDialogCallback;
                    lpCallbackData: LONG_PTR;
                    cxWidth: UINT  // width of the Task Dialog's client area in DLU's.
                                   // If 0, Task Dialog will calculate the ideal width.
                  );
              );
      end;
      {$EXTERNALSYM _TASKDIALOGCONFIG}
      _TASKDIALOGCONFIG = TASKDIALOGCONFIG;
      PTaskDialogConfig = ^TTaskDialogConfig;
      TTaskDialogConfig = TASKDIALOGCONFIG;

    该结构体其实是从MSDN里翻译过来的, 定义在CommCtrl.h 头文件里(需要Windows Vista, Windows Server 2008及以上版本, 我是用Windows 7 64位进行的测试), 详细说明可以查看MSDN.

    TCustomTaskDialog.DoExecute 填充完LTaskDialog结构体后, 直接调用:

    1
    2
    Result := TaskDialogIndirect(LTaskDialog, {$IFNDEF CLR}@{$ENDIF}LModalResult,
          {$IFNDEF CLR}@{$ENDIF}LRadioButton, {$IFNDEF CLR}@{$ENDIF}LVerificationChecked) = S_OK;

    TaskDialogIndirect显示对话框, TaskDialogIndirect定义在CommCtrl单元, 其代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    { Task Dialog }
     
    var
      _TaskDialogIndirect: function(const pTaskConfig: TTaskDialogConfig;
        pnButton: PInteger; pnRadioButton: PInteger;
        pfVerificationFlagChecked: PBOOL): HRESULT; stdcall;
     
      _TaskDialog: function(hwndParent: HWND; hInstance: HINST;
        pszWindowTitle: LPCWSTR; pszMainInstruction: LPCWSTR; pszContent: LPCWSTR;
        dwCommonButtons: DWORD; pszIcon: LPCWSTR; pnButton: PInteger): HRESULT; stdcall;
     
    function TaskDialogIndirect(const pTaskConfig: TTaskDialogConfig;
      pnButton: PInteger; pnRadioButton: PInteger; pfVerificationFlagChecked: PBOOL): HRESULT;
    begin
      if Assigned(_TaskDialogIndirect) then
        Result := _TaskDialogIndirect(pTaskConfig, pnButton, pnRadioButton,
          pfVerificationFlagChecked)
      else
      begin
        InitComCtl;
        Result := E_NOTIMPL;
        if ComCtl32DLL <> 0 then
        begin
          @_TaskDialogIndirect := GetProcAddress(ComCtl32DLL, 'TaskDialogIndirect');
          if Assigned(_TaskDialogIndirect) then
            Result := _TaskDialogIndirect(pTaskConfig, pnButton, pnRadioButton,
              pfVerificationFlagChecked)
        end;
      end;
    end;

    查看代码知道, TaskDialogIndirect 直接调用ComCtrl32.Dll里的函数:TaskDialogIndirect  显示对话框. 通过查询MSDN了解TaskDialogIndirect API的用途与用法:

    The TaskDialogIndirect function creates, displays, and operates a task dialog. The task dialog contains application-defined icons, messages, title, verification check box, command links, push buttons, and radio buttons. This function can register a callback function to receive notification messages.

    函数TaskDialogIndirect 用于创建, 显示, 运行一个任务对话框, 这个任务对话框可以包括由应用程序定义的图标,消息,标题,复选框,按钮,单选框. 该函数还可以接收一个回调函数用于接收通知信息

    看到这里你或许会问:

    如果我的系统是xp或其他低于Vista, server2008的系统呢? 由上文中可知, 如果是低版本的系统, 则调用DoMessageDlgPosHelp 函数进行对话框显示, 调用代码如下:

    1
    2
    Result := DoMessageDlgPosHelp(CreateMessageDialog(Msg, DlgType, Buttons),
          HelpCtx, X, Y, HelpFileName);

    DoMessageDlgPosHelp代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function DoMessageDlgPosHelp(MessageDialog: TForm; HelpCtx: Longint; X, Y: Integer;
      const HelpFileName: string): Integer;
    begin
      with MessageDialog do
        try
          HelpContext := HelpCtx;
          HelpFile := HelpFileName;
          if X >= 0 then Left := X;
          if Y >= 0 then Top := Y;
          if (Y < 0) and (X < 0) then Position := poScreenCenter;
          Result := ShowModal;
        finally
          Free;
        end;
    end;

    从DoMessageDlgPosHelp代码中可见, 该函数只是简单的将传递进来的TForm以模式窗口的形式显示在指定的位置.

    下面是CreateMessageDialog代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    function CreateMessageDialog(const Msg: string; DlgType: TMsgDlgType;
      Buttons: TMsgDlgButtons; DefaultButton: TMsgDlgBtn): TForm;
    const
      mcHorzMargin = 8;
      mcVertMargin = 8;
      mcHorzSpacing = 10;
      mcVertSpacing = 10;
      mcButtonWidth = 50;
      mcButtonHeight = 14;
      mcButtonSpacing = 4;
    var
      DialogUnits: TPoint;
      HorzMargin, VertMargin, HorzSpacing, VertSpacing, ButtonWidth,
      ButtonHeight, ButtonSpacing, ButtonCount, ButtonGroupWidth,
      IconTextWidth, IconTextHeight, X, ALeft: Integer;
      B, CancelButton: TMsgDlgBtn;
    {$IF DEFINED(CLR)}
      IconID: Integer;
    {$ELSE}
      IconID: PChar;
    {$IFEND}
      TextRect: TRect;
      LButton: TButton;
    begin
      Result := TMessageForm.CreateNew(Application);
      with Result do
      begin
        BiDiMode := Application.BiDiMode;
        BorderStyle := bsDialog;
        Canvas.Font := Font;
        KeyPreview := True;
        PopupMode := pmAuto;
        Position := poDesigned;
        OnKeyDown := TMessageForm(Result).CustomKeyDown;
        DialogUnits := GetAveCharSize(Canvas);
        HorzMargin := MulDiv(mcHorzMargin, DialogUnits.X, 4);
        VertMargin := MulDiv(mcVertMargin, DialogUnits.Y, 8);
        HorzSpacing := MulDiv(mcHorzSpacing, DialogUnits.X, 4);
        VertSpacing := MulDiv(mcVertSpacing, DialogUnits.Y, 8);
        ButtonWidth := MulDiv(mcButtonWidth, DialogUnits.X, 4);
        for B := Low(TMsgDlgBtn) to High(TMsgDlgBtn) do
        begin
          if B in Buttons then
          begin
            if ButtonWidths[B] = 0 then
            begin
              TextRect := Rect(0,0,0,0);
              Windows.DrawText( canvas.handle,
    {$IF DEFINED(CLR)}
                ButtonCaptions[B], -1,
    {$ELSE}
                PChar(LoadResString(ButtonCaptions[B])), -1,
    {$IFEND}
                TextRect, DT_CALCRECT or DT_LEFT or DT_SINGLELINE or
                DrawTextBiDiModeFlagsReadingOnly);
              with TextRect do ButtonWidths[B] := Right - Left + 8;
            end;
            if ButtonWidths[B] > ButtonWidth then
              ButtonWidth := ButtonWidths[B];
          end;
        end;
        ButtonHeight := MulDiv(mcButtonHeight, DialogUnits.Y, 8);
        ButtonSpacing := MulDiv(mcButtonSpacing, DialogUnits.X, 4);
        SetRect(TextRect, 0, 0, Screen.Width div 2, 0);
        DrawText(Canvas.Handle, Msg, Length(Msg)+1, TextRect,
          DT_EXPANDTABS or DT_CALCRECT or DT_WORDBREAK or
          DrawTextBiDiModeFlagsReadingOnly);
        IconID := IconIDs[DlgType];
        IconTextWidth := TextRect.Right;
        IconTextHeight := TextRect.Bottom;
    {$IF DEFINED(CLR)}
        if DlgType <> mtCustom then
    {$ELSE}
        if IconID <> nil then
    {$IFEND}
        begin
          Inc(IconTextWidth, 32 + HorzSpacing);
          if IconTextHeight < 32 then IconTextHeight := 32;
        end;
        ButtonCount := 0;
        for B := Low(TMsgDlgBtn) to High(TMsgDlgBtn) do
          if B in Buttons then Inc(ButtonCount);
        ButtonGroupWidth := 0;
        if ButtonCount <> 0 then
          ButtonGroupWidth := ButtonWidth * ButtonCount +
            ButtonSpacing * (ButtonCount - 1);
        ClientWidth := Max(IconTextWidth, ButtonGroupWidth) + HorzMargin * 2;
        ClientHeight := IconTextHeight + ButtonHeight + VertSpacing +
          VertMargin * 2;
        Left := (Screen.Width div 2) - (Width div 2);
        Top := (Screen.Height div 2) - (Height div 2);
        if DlgType <> mtCustom then
    {$IF DEFINED(CLR)}
          Caption := Captions[DlgType] else
          Caption := Application.Title;
        if DlgType <> mtCustom then
    {$ELSE}
          Caption := LoadResString(Captions[DlgType]) else
          Caption := Application.Title;
        if IconID <> nil then
    {$IFEND}
          with TImage.Create(Result) do
          begin
            Name := 'Image';
            Parent := Result;
            Picture.Icon.Handle := LoadIcon(0, IconID);
            SetBounds(HorzMargin, VertMargin, 32, 32);
          end;
        TMessageForm(Result).Message := TLabel.Create(Result);
        with TMessageForm(Result).Message do
        begin
          Name := 'Message';
          Parent := Result;
          WordWrap := True;
          Caption := Msg;
          BoundsRect := TextRect;
          BiDiMode := Result.BiDiMode;
          ALeft := IconTextWidth - TextRect.Right + HorzMargin;
          if UseRightToLeftAlignment then
            ALeft := Result.ClientWidth - ALeft - Width;
          SetBounds(ALeft, VertMargin,
            TextRect.Right, TextRect.Bottom);
        end;
        if mbCancel in Buttons then CancelButton := mbCancel else
          if mbNo in Buttons then CancelButton := mbNo else
            CancelButton := mbOk;
        X := (ClientWidth - ButtonGroupWidth) div 2;
        for B := Low(TMsgDlgBtn) to High(TMsgDlgBtn) do
          if B in Buttons then
          begin
            LButton := TButton.Create(Result);
            with LButton do
            begin
              Name := ButtonNames[B];
              Parent := Result;
    {$IF DEFINED(CLR)}
              Caption := ButtonCaptions[B];
    {$ELSE}
              Caption := LoadResString(ButtonCaptions[B]);
    {$IFEND}
              ModalResult := ModalResults[B];
              if B = DefaultButton then
              begin
                Default := True;
                ActiveControl := LButton;
              end;
              if B = CancelButton then
                Cancel := True;
              SetBounds(X, IconTextHeight + VertMargin + VertSpacing,
                ButtonWidth, ButtonHeight);
              Inc(X, ButtonWidth + ButtonSpacing);
              if B = mbHelp then
                OnClick := TMessageForm(Result).HelpButtonClick;
            end;
          end;
      end;
    end;

    由代码可见, CreateMessageDialog只是创建了一个TMessageForm, 然后动态地添加了一些设置. 写到这里或许可以解答一些人的问题: 对话框是不是一个窗口? 答案是:是.

    你还可能会问: 为什么对话框可以停留在那一行代码直到用户操作完毕后再往下执行, 这里就需要了解一下模态窗口的知识:  请参见这篇文章  Delphi ShowModal解析

    参考:

    http://www.cnblogs.com/neugls/archive/2011/09/14/2176733.html

  • 相关阅读:
    java生成验证码
    SpringBoot定时任务
    事务管理
    Windows 2008 Server R2双网卡负载均衡
    HP Proliant DL580 gen9 阵列卡P440AR 高速缓存 被禁用
    Kali Debian 修改时区
    First Py From Py
    C++头文件#include<bits/stdc++.h>
    排序算法
    运算符优先级
  • 原文地址:https://www.cnblogs.com/findumars/p/3493643.html
Copyright © 2020-2023  润新知