如何在标题栏上添加自己的按钮

发布时间:2011年1月1日 作者:未知 查看次数:1336

如何在标题栏上添加自己的按钮


如何在标题栏上添加自己的按钮



例子

原理

int GetSystemMetrics( 
int nIndex // system metric or configuration setting to retrieve 
);

SM_CXFRAME 窗口边框宽度 
SM_CYFRAME 窗口边框高度 
SM_CXSIZE 标题栏上的按钮宽度 
SM_CYSIZE 标题栏上的按钮高度

按钮显示出来后还要能响应鼠标消息才能真正的起到按钮的作用,我们在这处理鼠标按下的消息,也就是 WM_NCLBUTTONDOWN,还有 WM_NCLBUTTONUP 等。想要实现什么功能都可以在消息里面实现,示例程序中简单的调用函数 invoke SetWindowPos, hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE 设置窗口始终处在最上面。按钮按下后还需要绘制响应的按下的位图来模拟按钮按下的样子。

例子

.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
include c:\masm32\include\comctl32.inc

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\comctl32.lib

;****************************************************************************************

DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
DrawNcBtn proto :DWORD
GetNcBtnRect proto :DWORD,:DWORD

;****************************************************************************************

.data
DlgName db "DLG_MAIN",0
TooltipClassName db "Tooltips_class32",0
strNcBtn db "窗口总在最上",0

.data?
hInstance HINSTANCE ?
hToolTip dd ? ;工具提示控件的句柄
bPressed BOOL ? ;标题栏上的自绘按钮是否被按下

.const
IDC_STATIC equ -1
IDI_MAIN equ 900
IDB_PRESSED equ 901
IDB_UNPRESSED equ 902

;****************************************************************************************

.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,addr DlgName,NULL,addr DlgProc,0
invoke ExitProcess,eax
invoke InitCommonControls

;对话框窗口过程**************************************************************************

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
local rect:RECT
local pt:POINT
local ti:TOOLINFO

.IF uMsg==WM_CLOSE
invoke EndDialog,hWnd,NULL

.ELSEIF uMsg==WM_INITDIALOG
invoke LoadIcon, hInstance, IDI_MAIN
invoke SendMessage, hWnd, WM_SETICON, ICON_SMALL, eax

;创建工具提示控件
invoke CreateWindowEx, NULL, addr TooltipClassName, NULL, TTS_ALWAYSTIP, \
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, \
NULL, NULL, hInstance, NULL
mov hToolTip, eax
invoke SetWindowPos, hToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE

mov ti.cbSize, sizeof TOOLINFO
mov ti.uFlags, TTF_SUBCLASS
push hWnd
pop ti.hWnd

;计算自绘标题栏按钮位置
invoke GetNcBtnRect, hWnd, addr rect
invoke ScreenToClient, hWnd, addr rect

push rect.left
pop ti.rect.left
push rect.top
pop ti.rect.top

mov eax, ti.rect.left
add eax, 14
mov ti.rect.right, eax
mov eax, ti.rect.top
add eax, 14
mov ti.rect.bottom,eax

mov ti.lpszText, offset strNcBtn
invoke SendMessage, hToolTip, TTM_ADDTOOL, NULL, addr ti

mov bPressed, FALSE

.ELSEIF uMsg==WM_NCPAINT
invoke DefWindowProc, hWnd, uMsg, wParam, lParam ;这一句一定要放在前面
invoke DrawNcBtn, hWnd ;这一句一定要放在后面

.ELSEIF uMsg==WM_ACTIVATE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam ;这一句一定要放在前面
invoke DrawNcBtn, hWnd ;这一句一定要放在后面

.ELSEIF uMsg==WM_NCLBUTTONDOWN
mov eax, wParam
.IF eax==HTCAPTION ;判断鼠标是否在标题栏上(标题栏不包括左边的图标和右边的按钮所占的位置)
invoke GetNcBtnRect, hWnd, addr rect
invoke GetCursorPos, addr pt
invoke PtInRect, addr rect, pt.x, pt.y
.IF eax ;判断鼠标是否在自绘按钮范围内
.IF bPressed
mov bPressed, FALSE
invoke SetWindowPos, hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE
.ELSE
mov bPressed, TRUE
invoke SetWindowPos, hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE
.ENDIF
invoke DrawNcBtn, hWnd
.ELSE ;如果不是则调用缺省窗口过程处理
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF
.ELSE ;如果不在标题栏上(也就是为了能响应关闭等命令)
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF

.ELSE
mov eax,FALSE 
ret
.ENDIF
mov eax,TRUE
ret
DlgProc endp

;======================================
; 绘制标题栏按钮
;======================================
DrawNcBtn proc hWnd:HWND
local hDC, hMemDC:HDC
local rect:RECT
local rectWnd:RECT
local hBmp:HBITMAP

invoke GetWindowDC, hWnd ;取得窗口 DC
mov hDC, eax
invoke CreateCompatibleDC, hDC ;创建兼容 DC
mov hMemDC, eax

;根据按钮是否按下加载合适的位图
.IF bPressed
invoke LoadBitmap, hInstance, IDB_PRESSED
.ELSE
invoke LoadBitmap, hInstance, IDB_UNPRESSED
.ENDIF
mov hBmp, eax
invoke SelectObject, hMemDC, hBmp ;把位图选进兼容 DC

invoke GetWindowRect, hWnd, addr rectWnd ;取得窗口位置
invoke GetNcBtnRect, hWnd, addr rect ;计算自绘标题栏按钮位置

;计算自绘标题栏按钮在窗口中的位置
mov eax, rectWnd.left
neg eax
mov ebx, rectWnd.top
neg ebx
invoke OffsetRect, addr rect, eax, ebx

;把兼容 DC 中的位图贴到窗口 DC 上,实现按钮效果
mov eax, rect.right
sub eax, rect.left
mov ebx, eax
mov eax, rect.bottom
sub eax, rect.top
invoke BitBlt, hDC, rect.left, rect.top, ebx, eax, hMemDC, 0, 0, SRCCOPY

;释放资源
invoke DeleteDC, hMemDC
invoke ReleaseDC, hWnd, hDC

ret
DrawNcBtn endp

;=====================================================
; 计算自绘标题栏按钮的位置,在 addrRect 中返回位置
;=====================================================
GetNcBtnRect proc hWnd:HWND, addrRect:DWORD
local dxCXFRAME, dyCYFRAME, dxCXSIZE, dyCYSIZE:DWORD

invoke GetWindowRect, hWnd, addrRect

invoke GetSystemMetrics, SM_CXFRAME ;取得边框宽度
mov dxCXFRAME, eax
invoke GetSystemMetrics, SM_CYFRAME ;取得边框高度
mov dyCYFRAME, eax
invoke GetSystemMetrics, SM_CXSIZE ;取得标题栏按钮宽度
mov dxCXSIZE, eax
invoke GetSystemMetrics, SM_CYSIZE ;取得标题栏按钮高度
mov dyCYSIZE, eax

;具体计算过程,根据实际效果调整了几个值
mov edi, addrRect
assume edi:ptr RECT

mov eax, [edi].top ;上边界
add eax, dyCYFRAME
inc eax
mov [edi].top, eax

add eax, dyCYSIZE ;下边界
add eax, 2
mov [edi].bottom, eax

mov eax, [edi].right ;右边界
sub eax, dxCXFRAME
sub eax, dxCXSIZE
add eax, 2
mov [edi].right, eax

sub eax, dxCXSIZE ;左边界
mov [edi].left, eax

ret
GetNcBtnRect endp

end start

分析

invoke CreateWindowEx, NULL, addr TooltipClassName, NULL, TTS_ALWAYSTIP, \
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, \
NULL, NULL, hInstance, NULL
mov hToolTip, eax
invoke SetWindowPos, hToolTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE

mov ti.cbSize, sizeof TOOLINFO
mov ti.uFlags, TTF_SUBCLASS
push hWnd
pop ti.hWnd

;计算自绘标题栏按钮位置
invoke GetNcBtnRect, hWnd, addr rect
invoke ScreenToClient, hWnd, addr rect

push rect.left
pop ti.rect.left
push rect.top
pop ti.rect.top

mov eax, ti.rect.left
add eax, 14
mov ti.rect.right, eax
mov eax, ti.rect.top
add eax, 14
mov ti.rect.bottom,eax

mov ti.lpszText, offset strNcBtn
invoke SendMessage, hToolTip, TTM_ADDTOOL, NULL, addr ti

.ELSEIF uMsg==WM_NCPAINT
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
invoke DrawNcBtn, hWnd

.ELSEIF uMsg==WM_ACTIVATE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
invoke DrawNcBtn, hWnd

.ELSEIF uMsg==WM_NCLBUTTONDOWN
mov eax, wParam
.IF eax==HTCAPTION ;判断鼠标是否在标题栏上(标题栏不包括左边的图标和右边的按钮所占的位置)
invoke GetNcBtnRect, hWnd, addr rect
invoke GetCursorPos, addr pt
invoke PtInRect, addr rect, pt.x, pt.y
.IF eax ;判断鼠标是否在自绘按钮范围内
.IF bPressed
mov bPressed, FALSE
invoke SetWindowPos, hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE
.ELSE
mov bPressed, TRUE
invoke SetWindowPos, hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE
.ENDIF
invoke DrawNcBtn, hWnd
.ELSE ;如果不是则调用缺省窗口过程处理
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF
.ELSE ;如果不在标题栏上(也就是为了能响应关闭等命令)
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
.ENDIF

invoke GetWindowRect, hWnd, addr rectWnd ;取得窗口位置
invoke GetNcBtnRect, hWnd, addr rect ;计算自绘标题栏按钮位置

;计算自绘标题栏按钮在窗口中的位置
mov eax, rectWnd.left
neg eax
mov ebx, rectWnd.top
neg ebx
invoke OffsetRect, addr rect, eax, ebx




CopyRight (C) 2001-2002 一块三毛钱

 



版权所有!www.sieye.cn
E.Mail:sieye@sohu.com QQ:66697110