程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi完全時尚手冊之CoolBar篇---實現CoolBar的新特性Chevron

Delphi完全時尚手冊之CoolBar篇---實現CoolBar的新特性Chevron

編輯:Delphi

Delphi 完全時尚手冊之 CoolBar 篇
---實現 CoolBar 的新特性 Chevron

    我們發現到從 IE 5.0 以後,IE 的工具欄具有了一個新特性:當 IE 窗口縮小,使得工具欄上的按鈕不能完全顯示時,工具欄右邊會出現一個小按鈕(M$ 叫它 Chevron,實際上這是 CoolBar 的新特性),點擊後出現一下拉列表,顯示出被隱藏的按鈕。這大大方便了我們對工具欄的使用。


    那我們如何使 Delphi 的 TCoolBar 控件具有這個特性呢?經過我一個通宵的查閱資料(MSDN)、“潛心研究”(啊,誰扔我雞蛋...),終於做出來了!好東西不敢獨吞,拿出來與大家分享。下面就說說怎麼具體實現它。(以下代碼在 Delphi 6 下完成)

第一步:改造 Delphi 的 TCoolBand 類

    建議在進行這步前先備份 ComCtrls.pas 文件。如果將 CoolBar 中某個 Band 的 Style 中加上 RBBS_USECHEVRON 這個值,那麼當這個 Band 的寬度小於某個給定的值時它就會顯示一個下拉按鈕(Chevron)。下面來改 ComCtrls.pas 這個文件來實現這個功能。

  在 TCoolBand 的 Published 部分增加兩個屬性(Property):

  //Modified by Joe Huang
  property DealWidth: Integer read FDealWidth write SetDealWidth; {用來告訴 Band 當 Band 的寬度小於多少時顯示下拉按鈕(Chevron)}
  property UseChevron: Boolean read FUseChevron write SetUseChevron; {用來決定 Band 是否使用這個功能}
  //End

  兩個屬性的寫方法如下:

  procedure TCoolBand.SetUseChevron(const Value: Boolean);
  begin
    FUseChevron := Value;
    CoolBar.UpdateBands;
  end;
  procedure TCoolBand.SetDealWidth(const Value: Integer);
  begin
    FDealWidth := Value;
    CoolBar.UpdateBands;
  end;

    對 TCoolBand 的改動完成,但有一點要提醒的,這兩個屬性雖然在 Publish 部分,但在設計期並不能看到(我也不知道是怎麼搞的)。所以我們只能在運行期間訪問到它們。

第二步:改造 Delphi 的 TCoolBar 控件

  在 ComCtrls.pas 中找到 TCoolBar.UpdateItem 方法,先為這個方法加個常數如下:

  //Modified by Joe Huang
  RBBS_USECHEVRON = $00000200;
  //End

  然後在這個方法中找到這一行(第一次出現 fMask := 的地方):

  fMask := RBBIM_STYLE or RBBIM_COLORS or RBBIM_SIZE or RBBIM_BACKGROUND or RBBIM_IMAGE or RBBIM_ID;

  在這行下加入下段代碼:

  //Modified by Joe Huang
  if (GetComCtlVersion >= ComCtlVersionIE5) and Band.UseChevron then {這個功能只在 IE5 後才有}
  begin
    fStyle := fStyle or RBBS_USECHEVRON;
    if Band.DealWidth > 0 then
    begin
      fMask := fMask or RBBIM_IDEALSIZE;
      cxIdeal := Band.DealWidth;
    end;
  end;
  //End

    OK! TCoolBar改造完成。為 ComCtrls.pas 生成 .dcu 文件,方法:把 ComCtrls.pas 文件拷貝到一個現有工程的目錄下,把它加到這個工程中,編譯這個工程就會得到它的 .dcu 文件,將個 .dcu 文件覆蓋(注意備份) Delphi 原來的,在 Delphi6Lib 目錄下。

第三步:在一工程中具體實現。

    打開 Delphi6,新建一工程,在 Form1 上放入 CoolBar1,再在 CoolBar1 上面放入 ToolBar1,CoolBar1 會自動產生一 Band。設置 ToolBar1 的 AutoSize 為 True,Wrapable 為 False 並在其上放入你的按鈕。設置 CoolBar1 的 AutoSize 為 True, ShowText 為 False(注意:將這屬性置為 False 可以為我提供一個利用 Band.Text 屬性定位 Band 的方法。在 CoolBar1 上有多個 Band 時定位 Band 是必須的。你也可以采用你自己的定位方式。),設置放有 ToolBar1 的 Band 的 Text 為 MyToolBand。下面開始寫代碼。


  在 Form1 單元的 uses 後加入 CommCtrl 單元;在 Implementation 部分寫兩個自定義方法:

  {用來取得 ToolBar1 上所有可見按鈕的總寬度。
  用來為我們前面給 TCoolBand 增加的屬性 DealWidth 賦值,
  即是當 Band 的寬度小於所有按鈕的總寬度時顯示下拉按鈕(Chevron)。}
  function GetTBButtonsWidth(AToolBar: TToolBar): Cardinal;
  var
    ARect, ButtonRect: TRect;
    TBCount, I: Integer;
  begin
ARect := Rect(0, 0, 0, 0); TBCount := AToolBar.Perform(TB_BUTTONCOUNT, 0, 0); for I := 0 to TBCount - 1 do begin AToolBar.Perform(TB_GETITEMRECT, I, Integer(@ButtonRect)); ARect.Right := ARect.Right + (ButtonRect.Right - ButtonRect.Left); end; Result := Abs(ARect.Right - ARect.Left); end; {用來定位 Band 參數 BandText 為你所要定位 Band 的 Text 屬性} function GetCoolBand(BandText: string; ACoolBar: TCoolBar): Integer; var I: Integer; begin Result := -1; for I := 0 to ACoolBar.Bands.Count - 1 do begin if ACoolBar.Bands.Items[I].Text = BandText then begin Result := I; Break; end; end; end;

    由於 CoolBar1 上 Band 可以改變位置(當有多個 Band 時),所以我們需要一個變量來存儲放有 ToolBar1 的 Band 的當前位置(後面會提到如何捕捉到 Band 的位置變化)。

  在 Private 部分定義一變量:

  private
    CoolBandIndex: Integer;

  在 Form1 的 OnShow 事件加入如下代碼:

  CoolBandIndex := GetCoolBand(MyToolBand, CoolBar1);   {定位 Band 的位置}
  CoolBar1.Bands.Items[CoolBandIndex].UseChevron := True;   {我們自己加的屬性}
  CoolBar1.Bands.Items[CoolBandIndex].DealWidth := GetTBButtonsWidth(ToolBar1);    {我們自己加的屬性}

    現在大家可以運行一下程序了,然後縮放 Form1 使 ToolBar1 上部分按鈕被遮住,看下拉按鈕(Chevron)是不是出來了!(什麼?沒有!趕快檢查一下前面各步做得是否正確)

    大家可能注意到了一個問題:ToolBar1 上按鈕可能被遮住了一半,另一半還顯示在外面,能不能使一個按鈕一旦部分被遮住後,整個按鈕不顯示呢?我發現 Delphi7 中的 ToolBar 中多了一個屬性 HideClippedButtons,就是干這事的,這個屬性只在ME、2000、XP下起作用,但 Delphi6 卻沒有這個屬性。有興趣的可以參照 Delphi7 改一下,很容易的。

    各位注意了,重頭戲來了。如何點擊這個下拉按鈕(Chevron)使被遮住的按鈕顯示出來呢?還有我們前面提到的當 Band 改變位置時如何能通知我們呢?答案是消息!當我們點擊下拉按鈕(Chevron)時 CoolBar 會給它的父窗口發送 RBN_CHEVRONPUSHED 消息;當改變 Band 的位置會發送 RBN_LAYOUTCHANGED 消息。實際上這兩個消息是附加在 WM_NOTIFY 消息中的。下面我們就來在 Form1 的窗口函數中攔截這兩個消息(一般來說 Form1 是 CoolBar1 的父,如果你將 CoolBar1 放在其他容器控件中,則要在相應的窗口函數中攔截,原理相同)。

  在 Private 部分定義兩個變量及一過程:

  private
FClientInstance : TFarProc; FPrevClientProc : TFarProc; procedure NewWindowProc(var message:TMessage);

  在將 NewWindowProc 這個過程的實現前先在 Form1 的 OnShow 事件中加入代碼(這段代碼寫在 OnShow 中所有代碼的前面):

  procedure TForm1.FormShow(Sender: TObject);
  begin
    {$Warnings Off}
    FClientInstance := MakeObjectInstance(NewWindowProc);
    FPrevClientProc := Pointer(GetWindowLong(Form1.Handle, GWL_WNDPROC));
    SetWindowLong(Form1.Handle, GWL_WNDPROC, LongInt(FClientInstance));   {替換 Form1 的窗口函數}
    {$Warnings On}
    {... ...}
  end;

  再在 Form1 上放一 PopupMenu1,設置它的 Alignment 為 paRight。用來顯示被遮住的按鈕。

  窗口函數 NewWindowProc 的實現如下:

  procedure TForm1.NewWindowProc(var message: TMessage);
  type
    {封裝一 TNMREBARCHEVRON 結構。這個結構隨 RBN_CHEVRONPUSHED 消息發送。}
    PNMREBARCHEVRON = ^TNMREBARCHEVRON;
    TNMREBARCHEVRON = record
      hdr: TNMHDR;
      uBand: UINT;
      wID: UINT;
      lParam: LPARAM;
      rc: TRECT;
      lParamNM: LPARAM ;
    end;
      
  const
    RBN_CHEVRONPUSHED = RBN_FIRST - 10;
      
  var
    ANMHDR: PNMHDR;
    ANMREBARCHEVRON: PNMREBARCHEVRON;
    ScreenRect: TRect;
    FirstClipButton, I: Integer;
    AMenuItem: TMenuItem;
    {這個函數用來得到 ToolBar1 上被遮住按鈕中最左邊那個的位置(Index)。}
    function GetFirstClipButton(ACoolBar: TCoolBar; AToolBar: TToolBar): Integer;
    var
      ButtonRect: TRect;
      TBCount, I, TempWidth: Integer;
    begin
      Result := -1;
      TempWidth := 0;
      TBCount := AToolBar.Perform(TB_BU						

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