分享
 
 
 

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

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

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

Windows 的窗口似乎总是一成不变的,标题栏也是那个老样子。如果用过 ReadBook 的人可能会注意到,在它的标题栏上多出来了两个按钮:全屏、缩小到托盘。跟最小化、最大化、关闭按钮排列在一起,而且功能也相近,让人觉得它们本来就应该是在那的。下面介绍实现原理和细节。图示为示例程序的运行效果,例子

原理

标题栏上的按钮并不是标准的按钮子窗口,按钮的按下与弹起效果完全就是两幅位图模拟出来的。为此,我们需要处理 WM_NCPAINT 和 WM_ACTIVATE 消息。首先计算出按钮的位置,利用 GetWindowRect 得到窗口的大小位置,再调用函数 GetSystemMetrics 得到标题栏的高度等系统参数。函数原型比较简单:

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

为工具提示控件创建“工具”区域,在这创建的是将窗口的一部分作为“工具”区域,所以要填充 TOOLINFO 的 rect 域,首先取得自绘按钮的大小位置,这时的值是相对于整个屏幕的,要把它转换为相对窗口位置的值。接着设置提示信息,最后把“工具”区域和工具提示控件关联起来。

.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

处理 WM_NCPAINT 和 WM_ACTIVATE 消息。调用 DrawNcBtn 函数前需要先调用 DefWindowProc 缺省窗口来处理这两个消息,然后我们再将我们的按钮画上去。否则标题栏会看不见!

.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

响应鼠标在标题栏上按下的消息,实现按钮的功能。判断鼠标是否是在自绘按钮上按下,在根据按钮的状态分别处理。我们不处理的消息要调用 DefWindowProc 缺省窗口过程来处理。

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 一块三毛钱

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有