程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi中實現可以更改大小的對話框

Delphi中實現可以更改大小的對話框

編輯: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去掉,結果象這樣:窗口可以修改大小,但左上角有圖標,:


  圖 1 帶圖標的對話框


  注意左上角有圖標。

  而我們的目標則是下面的兩種效果,左上角都沒有圖標,但窗口都可以修改大小。


  圖 2 打開文件對話框

圖 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消息處理過程沒有觸發)。問題出在哪裡呢?

圖 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://www.zocsoft.com/temp/Resizable_Dialog.rar

  

  5、參考資料:

  MSDN: How To Design a Resizable MFC Property Sheet

  

  引用地址:《Delphi中實現可以更改大小的對話框


  
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved