https://bbs.csdn.net/topics/245498
-- 大家在使用某些软件的过程中,有没有注意到有些软件有一些很有趣的东西。比如说在主窗口的标题栏上居然有一个按钮。在Internet中随处可见这样的小控件。按钮怎么可以加入到非客户区(Client)呢? ---- 在这里,最关键的一点就是,大家不要被传统知识误导:真的认为它是一个按钮。有名柄(handle)的控件当然不能放在标题栏上了。有经验的程序员用Spy++跟踪一下的话,马上就会发现其中的秘密。它并不是一个按钮,只不过是处理成按钮的样子罢了。 ---- 既然知道了所以然,那么我们为什么不能自己来做一个呢,当然没问题,下面我们就用Delphi来实现它,讲注意我的注解。 ---- 在具体实例之前,我们应该知道几个关于标题栏的重要的消息: ---- WM_NCPAINT:重画标题栏消息。我们必须截住它,可以在这里重画按钮; ---- WM_NCLBUTTONDOWN:在标题栏上按下鼠标左键消息。我们可以截住它,在标题栏上画出按钮按下的样子,并且可以在其中进行自已的单击事件的处理,使得它像一个按钮; ---- WM_NCLBUTTONUP:在标题栏上释放鼠标左键消息。我们可以截住它,在标题栏上画出按钮弹起的样子; ---- WM_NCLBUTTONDBLCLK:在标题栏上双击鼠标左键消息。我们可以截住它,当在按钮区域双击时,我们就该使其无效,从而避免窗体执行最大化和还原操作。 ---- WM_NCRBUTTONDOWN:在标题栏上按下鼠标右键消息。我们可以截住它,当在按钮区域双击时,我们就该使其无效,从而避免弹出窗体按制菜单。 ---- WM_NCMOUSEMOVE:在标题栏上移动鼠标消息。我们可以截住它,当鼠标移出按钮区域时,我们就必须画出按钮没有被按下,即凸起时的样子。 ---- WM_NCACTIVATE:当标题栏在激活与非激活之间切换时收到该消息。我们可以截住它,当该窗口处理激活状态时,我们可以做一些事情,比如说将我们的标题栏按钮上的字体变灰或变黑来指示该窗口的当前状态。下面我没有加入该项功能,如果大家感兴趣的话,可以自己完成。 ---- (大家从这里可以发现,标题栏的消息都是WM_NC开头的) //////////////////////////////////////////////////////////////////////////////// unit Capbtn; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, Buttons; type TTitleBtnForm = class(TForm) procedure FormResize(Sender: TObject); private TitleButton : TRect; procedure DrawTitleButton; {Paint-related messages} procedure WMSetText(var Msg : TWMSetText); message WM_SETTEXT; procedure WMNCPaint(var Msg : TWMNCPaint); message WM_NCPAINT; procedure WMNCActivate(var Msg : TWMNCActivate); message WM_NCACTIVATE; {Mouse down-related messages} procedure WMNCHitTest(var Msg : TWMNCHitTest); message WM_NCHITTEST; procedure WMNCLButtonDown(var Msg : TWMNCLButtonDown); message WM_NCLBUTTONDOWN; function GetVerInfo : DWORD; end; var TitleBtnForm: TTitleBtnForm; const htTitleBtn = htSizeLast + 1; implementation {$R *.DFM} procedure TTitleBtnForm.DrawTitleButton; var bmap : TBitmap; {Bitmap to be drawn - 16 X 16 : 16 Colors} XFrame, {X and Y size of Sizeable area of Frame} YFrame, XTtlBit, {X and Y size of Bitmaps in caption} YTtlBit : Integer; begin {Get size of form frame and bitmaps in title bar} XFrame := GetSystemMetrics(SM_CXFRAME); YFrame := GetSystemMetrics(SM_CYFRAME); XTtlBit := GetSystemMetrics(SM_CXSIZE); YTtlBit := GetSystemMetrics(SM_CYSIZE); {$IFNDEF WIN32} TitleButton := Bounds(Width - (3 * XTtlBit) - ((XTtlBit div 2) - 2), YFrame - 1, XTtlBit + 2, YTtlBit + 2); {$ELSE} {Delphi 2.0 positioning} if (GetVerInfo = VER_PLATFORM_WIN32_NT) then TitleButton := Bounds(Width - (3 * XTtlBit) - ((XTtlBit div 2) - 2), YFrame - 1, XTtlBit + 2, YTtlBit + 2) else TitleButton := Bounds(Width - XFrame - 4*XTtlBit + 2, XFrame + 2, XTtlBit + 2, YTtlBit + 2); {$ENDIF} Canvas.Handle := GetWindowDC(Self.Handle); {Get Device context for drawing} try {Draw a button face on the TRect} DrawButtonFace(Canvas, TitleButton, 1, bsAutoDetect, False, False, False); bmap := TBitmap.Create; bmap.LoadFromFile('help.bmp'); with TitleButton do {$IFNDEF WIN32} Canvas.Draw(Left + 2, Top + 2, bmap); {$ELSE} if (GetVerInfo = VER_PLATFORM_WIN32_NT) then Canvas.Draw(Left + 2, Top + 2, bmap) else Canvas.StretchDraw(TitleButton, bmap); {$ENDIF} finally ReleaseDC(Self.Handle, Canvas.Handle); bmap.Free; Canvas.Handle := 0; end; end; {Paint triggering events} procedure TTitleBtnForm.WMNCActivate(var Msg : TWMNCActivate); begin Inherited; DrawTitleButton; end; procedure TTitleBtnForm.FormResize(Sender: TObject); begin Perform(WM_NCACTIVATE, Word(Active), 0); end; {Painting events} procedure TTitleBtnForm.WMNCPaint(var Msg : TWMNCPaint); begin Inherited; DrawTitleButton; end; procedure TTitleBtnForm.WMSetText(var Msg : TWMSetText); begin Inherited; DrawTitleButton; end; {Mouse-related procedures} procedure TTitleBtnForm.WMNCHitTest(var Msg : TWMNCHitTest); begin Inherited; {Check to see if the mouse was clicked in the area of the button} with Msg do if PtInRect(TitleButton, Point(XPos - Left, YPos - Top)) then Result := htTitleBtn; end; procedure TTitleBtnForm.WMNCLButtonDown(var Msg : TWMNCLButtonDown); begin inherited; if (Msg.HitTest = htTitleBtn) then ShowMessage('You pressed the new button'); end; function TTitleBtnForm.GetVerInfo : DWORD; var verInfo : TOSVERSIONINFO; begin verInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo); if GetVersionEx(verInfo) then Result := verInfo.dwPlatformID; {Returns: VER_PLATFORM_WIN32s Win32s on Windows 3.1 VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95 VER_PLATFORM_WIN32_NT Windows NT } end; end.