仿制金山毒霸的标题栏
千变一律的界面总是让人觉得缺乏新意,在功能相当的一系列软件中大家肯定偏爱那些界面看起来赏心悦目的,如果我们能够给自己的软件加一个与众不同标题栏是不是也能为我们的软件增色不少呢!下面是我仿照金山毒霸的界面做的一个标题栏,运行效果如下。例子
原理
要定制标题栏,我们可以在 Windows 提供的标题栏上绘制自己所要实现的效果。但因为总的框架在那,再怎么绘制也不过是加几个控件,写一些彩色的字,像上图这样的效果似乎很难实现。在这我采用的方法是:去掉缺省的标题栏,画一个自己的标题栏。
在定制对话框模板时指定 WS_POPUP 风格就可以得到一个没有标题栏的对话框,也可以在对话框初始化时通过 SetWindowLong 设置窗口风格来去掉标题栏。
缺省提供的标题栏没了我们就需要自己画一个标题栏。分为两个部分:外表的实现,功能的实现。通过在 Static 控件上加载位图就能够实现标题栏的外表显示,如上图,一共采用了三个 Static 控件来加载标题栏位图、最小化按钮位图、关闭按钮位图。
功能的实现有:窗口的拖动,关闭按钮的响应,最小化按钮的响应。窗口的拖动我们只需要在鼠标左键按下时判断是否是在我们绘制的标题栏范围内,如果在,就给窗口发送 WM_NCLBUTTONDOWN 消息。按钮的响应有鼠标移到按钮上按钮变亮,鼠标按下时按钮产生按下效果等。首先子类化两个 Static 按钮控件,在其中处理 WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP 等消息,根据不同的消息和鼠标是否按下等来加载不同的位图,从而产生按钮加亮、按钮按下等不同效果。
标题栏画好后如果还继续采用窗口缺省的灰灰的界面势必会影响整体效果,所以我们通过处理 WM_CTLCOLORDLG 消息来改变背景颜色。
例子
.386
.model flat,stdcall
option casemap:none
;****************************************************************************************
include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\gdi32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
;****************************************************************************************
RGB macro red,green,blue
xor eax,eax
mov ah,blue
shl eax,8
mov ah,green
mov al,red
endm
;****************************************************************************************
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
CloseBtnProc proto :DWORD,:DWORD,:DWORD,:DWORD
MinBtnProc proto :DWORD,:DWORD,:DWORD,:DWORD
;****************************************************************************************
.data
DlgName db "DLG_MAIN",0
szCaption db "仿制金山毒霸的标题栏",0
.data?
hInstance HINSTANCE ?
hBmp1 dd ?
hBmp2 dd ?
hBmp3 dd ?
hBmp4 dd ?
hBmp5 dd ?
hBmp6 dd ?
hBmp7 dd ?
hCaption dd ?
hClose dd ?
hMin dd ?
bMouseDown dd ?
.const
IDI_MAIN equ 900
IDB_BITMAP1 equ 901
IDB_BITMAP2 equ 902
IDB_BITMAP3 equ 903
IDB_BITMAP4 equ 904
IDB_BITMAP5 equ 905
IDB_BITMAP6 equ 906
IDB_BITMAP7 equ 907
IDC_STATIC_CAPTION equ 2001
IDC_STATIC_MIN equ 2002
IDC_STATIC_CLOSE equ 2003
;****************************************************************************************
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,addr DlgName,NULL,addr DlgProc,0
invoke ExitProcess,eax
;对话框窗口过程**************************************************************************
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
local pt:POINT
local rect:RECT
local hDC:HDC
.IF uMsg==WM_CLOSE
invoke EndDialog,hWnd,NULL
.ELSEIF uMsg==WM_INITDIALOG
invoke SendMessage, hWnd, WM_SETTEXT, 0, addr szCaption
;加载图像
invoke LoadBitmap, hInstance, IDB_BITMAP1
mov hBmp1, eax
invoke LoadBitmap, hInstance, IDB_BITMAP2
mov hBmp2, eax
invoke LoadBitmap, hInstance, IDB_BITMAP3
mov hBmp3, eax
invoke LoadBitmap, hInstance, IDB_BITMAP4
mov hBmp4, eax
invoke LoadBitmap, hInstance, IDB_BITMAP5
mov hBmp5, eax
invoke LoadBitmap, hInstance, IDB_BITMAP6
mov hBmp6, eax
invoke LoadBitmap, hInstance, IDB_BITMAP7
mov hBmp7, eax
;子类化“关闭”和“最小化”按钮
invoke GetDlgItem, hWnd, IDC_STATIC_CAPTION
mov hCaption, eax
invoke GetDlgItem, hWnd, IDC_STATIC_CLOSE
mov hClose, eax
invoke SetWindowLong, hClose, GWL_WNDPROC, addr CloseBtnProc
invoke SetWindowLong, hClose, GWL_USERDATA, eax
invoke GetDlgItem, hWnd, IDC_STATIC_MIN
mov hMin, eax
invoke SetWindowLong, hMin, GWL_WNDPROC, addr MinBtnProc
invoke SetWindowLong, hMin, GWL_USERDATA, eax
mov bMouseDown, FALSE
;重新设置窗口大小
invoke GetClientRect, hWnd, addr rect
sub rect.right, 2
invoke SetWindowPos, hWnd, 0, 0, 0, rect.right, rect.bottom, SWP_NOZORDER or SWP_NOMOVE
;画出窗口的边缘线
.ELSEIF uMsg==WM_PAINT
invoke GetDC, hWnd
mov hDC, eax
invoke GetClientRect, hWnd, addr rect
sub rect.right, 1
sub rect.bottom, 2
invoke MoveToEx, hDC, 0, 30, NULL
invoke LineTo, hDC, 0, rect.bottom
invoke MoveToEx, hDC, 0, rect.bottom, NULL
invoke LineTo, hDC, rect.right, rect.bottom
invoke MoveToEx, hDC, rect.right, rect.bottom, NULL
invoke LineTo, hDC, rect.right, 29
mov eax, FALSE
ret
;改变对话框的背景色,为了和标题栏协调起来
.ELSEIF uMsg==WM_CTLCOLORDLG
RGB 0F0h, 0F0h, 0F0h
invoke CreateSolidBrush, eax
ret
;拖动窗口
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke GetCursorPos, addr pt
invoke ScreenToClient, hWnd, addr pt
.IF pt.y<30 && pt.x<420
invoke SendMessage, hWnd, WM_NCLBUTTONDOWN, 2, 0
.ENDIF
.ELSE
mov eax,FALSE
ret
.ENDIF
mov eax,TRUE
ret
DlgProc endp
;“关闭”按钮窗口过程********************************************************************
CloseBtnProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
local pt:POINT
.IF uMsg==WM_NCHITTEST
mov eax, TRUE
ret
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp4
mov bMouseDown, TRUE
.ELSEIF uMsg==WM_MOUSEMOVE
invoke GetCursorPos, addr pt
invoke WindowFromPoint, pt.x, pt.y
.IF eax==hWnd
invoke SetCapture, hWnd
.IF bMouseDown
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp4
.ELSE
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp3
.ENDIF
.ELSE
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp2
.IF!bMouseDown
invoke ReleaseCapture
.ENDIF
.ENDIF
.ELSEIF uMsg==WM_LBUTTONUP
invoke GetCursorPos, addr pt
invoke WindowFromPoint, pt.x, pt.y
.IF eax==hWnd
.IF bMouseDown
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp3
invoke GetParent, hWnd
invoke SendMessage, eax, WM_CLOSE, 0, 0
.ENDIF
.ENDIF
invoke ReleaseCapture
mov bMouseDown, FALSE
.ELSE
invoke GetWindowLong, hWnd, GWL_USERDATA
invoke CallWindowProc, eax, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax,eax
ret
CloseBtnProc endp
MinBtnProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
local pt:POINT
.IF uMsg==WM_NCHITTEST
mov eax, TRUE
ret
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp7
mov bMouseDown, TRUE
.ELSEIF uMsg==WM_MOUSEMOVE
invoke GetCursorPos, addr pt
invoke WindowFromPoint, pt.x, pt.y
.IF eax==hWnd
invoke SetCapture, hWnd
.IF bMouseDown
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp7
.ELSE
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp6
.ENDIF
.ELSE
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp5
.IF!bMouseDown
invoke ReleaseCapture
.ENDIF
.ENDIF
.ELSEIF uMsg==WM_LBUTTONUP
invoke GetCursorPos, addr pt
invoke WindowFromPoint, pt.x, pt.y
.IF eax==hWnd
.IF bMouseDown
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp6
invoke GetParent, hWnd
invoke ShowWindow, eax, SW_MINIMIZE
.ENDIF
.ENDIF
invoke ReleaseCapture
mov bMouseDown, FALSE
.ELSE
invoke GetWindowLong, hWnd, GWL_USERDATA
invoke CallWindowProc, eax, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax,eax
ret
MinBtnProc endp
end start
分析
invoke GetDlgItem, hWnd, IDC_STATIC_CAPTION
mov hCaption, eax
invoke GetDlgItem, hWnd, IDC_STATIC_CLOSE
mov hClose, eax
invoke SetWindowLong, hClose, GWL_WNDPROC, addr CloseBtnProc
invoke SetWindowLong, hClose, GWL_USERDATA, eax
invoke GetDlgItem, hWnd, IDC_STATIC_MIN
mov hMin, eax
invoke SetWindowLong, hMin, GWL_WNDPROC, addr MinBtnProc
invoke SetWindowLong, hMin, GWL_USERDATA, eax
mov bMouseDown, FALSE
首先取得“关闭”按钮和“最小化”按钮的句柄,然后分别子类化以截获消息处理消息。
invoke GetClientRect, hWnd, addr rect
sub rect.right, 2
invoke SetWindowPos, hWnd, 0, 0, 0, rect.right, rect.bottom, SWP_NOZORDER or SWP_NOMOVE
改变窗口的大小,主要是为了匹配标题栏的宽度。
.ELSEIF uMsg==WM_PAINT
invoke GetDC, hWnd
mov hDC, eax
invoke GetClientRect, hWnd, addr rect
sub rect.right, 1
sub rect.bottom, 2
invoke MoveToEx, hDC, 0, 30, NULL
invoke LineTo, hDC, 0, rect.bottom
invoke MoveToEx, hDC, 0, rect.bottom, NULL
invoke LineTo, hDC, rect.right, rect.bottom
invoke MoveToEx, hDC, rect.right, rect.bottom, NULL
invoke LineTo, hDC, rect.right, 29
mov eax, FALSE
ret
处理 WM_PAINT 消息,分别在窗口的左边、下边和右边画上边界线,这样一来有一种平面效果正好匹配我们的标题栏。
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke GetCursorPos, addr pt
invoke ScreenToClient, hWnd, addr pt
.IF pt.y<30 && pt.x<420
invoke SendMessage, hWnd, WM_NCLBUTTONDOWN, 2, 0
.ENDIF
在鼠标按下时判断是不是在我们绘制的标题栏范围内,在这除去了右边“关闭”按钮和“最小化”按钮占去的位置。
.IF uMsg==WM_NCHITTEST
mov eax, TRUE
ret
返回 TRUE 从而实现鼠标消息的截获。
.ELSEIF uMsg==WM_LBUTTONDOWN
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp4
mov bMouseDown, TRUE
.ELSEIF uMsg==WM_MOUSEMOVE
invoke GetCursorPos, addr pt
invoke WindowFromPoint, pt.x, pt.y
.IF eax==hWnd
invoke SetCapture, hWnd
.IF bMouseDown
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp4
.ELSE
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp3
.ENDIF
.ELSE
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp2
.IF!bMouseDown
invoke ReleaseCapture
.ENDIF
.ENDIF
.ELSEIF uMsg==WM_LBUTTONUP
invoke GetCursorPos, addr pt
invoke WindowFromPoint, pt.x, pt.y
.IF eax==hWnd
.IF bMouseDown
invoke SendMessage, hWnd, STM_SETIMAGE, IMAGE_BITMAP, hBmp3
invoke GetParent, hWnd
invoke SendMessage, eax, WM_CLOSE, 0, 0
.ENDIF
.ENDIF
invoke ReleaseCapture
mov bMouseDown, FALSE
在鼠标按下时加载按下效果的位图,并设置标志位标志鼠标处在按下状态。在 WM_MOUSEMOVE 消息中先取得鼠标位置,判断是否在按钮上,如果在按钮上,则锁定鼠标消息,然后根据鼠标是否正按下来加载按钮按下的位图或按钮加亮的位图。如果不在按钮上,则加载正常的位图,然后判断鼠标是否正处在按下的状态,如果不是,则说明只是鼠标经过按钮上而已,可以释放鼠标;如果正处在按下状态,则不能释放鼠标,因为如果这时候释放鼠标,当鼠标移出按钮然后弹起时我们就接收不到消息,所以要留到 WM_LBUTTONUP 消息中释放鼠标。在 WM_LBUTTONUP 消息中也是先取得鼠标位置,判断是否在按钮上,如果在,再判断鼠标是否处在按下状态,接着就可以发送消息实现按钮的功能。最后释放鼠标,设置标志位标志鼠标已经弹起。
CopyRight (C) 2001-2002 一块三毛钱