admin 管理员组

文章数量: 1087135


2024年6月2日发(作者:二甲苯致癌几率大吗)

SDI 和 MDI 程序中对非客户区(标题栏、左右下边界)的美化

基本思路是重载 CMainFrame 类的 DefWindowProc()函数,并判断消息为:

WM_NCPAINT,WM_NCACTIVATE,WM_NOTIFY的时候,调用自己的绘制窗口标题栏的函

数。用 GetSystemMetrics(SM_CSFRAME)和 GetSystemMetrics(SM_CYFRAME)可以取得标

题栏的左上角的坐标。最大化,最小化的按钮自己画,如果不是在标准的位置,一定要记录

下他们的位置,并且在 WM_NCLBUTTONDOWN消息处理函数中判断是否是点击了按钮,

以做出相应的处理。系统图标也可以自己重新画。

主要任务有贴图(包括标题栏、左边界、右边界、下边界、系统图标、最大化、最小

化、

关闭按钮)、处理消息(屏蔽系统自带按钮、双击状态栏改变大小、鼠标停放在三个自绘按

钮上时改变按钮图标、单击自绘按钮时作出相应反应)。

一、响应的消息及重载的函数

响应的消息及重载的函数都在 CMainFrame 类中。响应 DefWindowProc 函数,在其

判断消息是不是 WM_NCPAINT、WM_MOVE、WM_NCACTIVATE、WM_NOTIFY,若是则

重画标题栏、左框架、右框架、下框架、最大化、最小化、关闭按钮(放在一个函数里)。

响应消息 WM_NCHITTEST,使鼠标位于自绘按钮时返回相应 hittest 值,同时屏蔽

自带

按钮的鼠标事件。简言之,当鼠标位于自绘按钮时,让 系统误以为鼠标位于相应按钮,

而当鼠标位于系统自带按钮时,让系统误以为鼠标只是位于标题栏。自绘图标与之类似,不

再赘述。

响应消息 WM_NCMOUSEMOVE,判断光标是不是位于自绘最大化、最小化、关闭按

区域,如是则重画相应的按钮。

响应消息 WM_NCLBUTTONDOWN,判断单击左键时鼠标是否位于自绘制的最大化、

最小化、关闭按钮或图标区域,如是则执行相应的按钮操作。

响应消息 WM_NCLBUTTONDBCLK,使双击标题栏时窗口能最大化或还原。

二、主要函数

LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM

lParam),在此函数内判断 WM_NCPAINT、WM_MOVE、WM_NCACTIVATE、WM_NOTIFY

消息,自绘框架。

自定义函数 void DrawFrame(CDC *pDC),用于绘制标题栏、左框架、右框架、下框架、

最大化、最小化、关闭按钮。

三、位图资源

标题栏位图 IDB_TITLEBAR

左右框架位图 IDB_LEFTANDRIGHT

下框架位图 IDB_BOTTOM

最小化按钮 IDB_MIN_NORMAL

IDB_MIN_FOCUS

最大化/恢复按钮 IDB_MAX_NORMAL

IDB_MAX_FOCUS

IDB_RESTORE_NORMAL

IDB_RESTORE_FOCUS

关闭按钮 IDB_EXIT_NORMAL

IDB_EXIT_FOCUS

四、主要变量

CRect m_rtButtExit; //关闭按钮位置

CRect m_rtButtMax; //最大化按钮位置

CRect m_rtButtMin; //最小化按钮位置

CRect m_rtIcon; //图标位置

五、具体实现细节

1、填充各框架:设置 CRect 变量 rtWnd, rtTitle, rtButtons,rtFrames分别保存窗口位置、

标题栏位置、关闭最大最小化按钮位置及左右下框架位置坐标。用函数

GetWindowRect(&rtWnd)获得窗口位置;用函数 GetSystemMetrics(SM_CXFRAME)获得

框架水平边缘厚度,GetSystemMetrics(SM_CYFRAME)获得框架竖直边缘的厚度,

GetSystemMetrics ( SM_CXSIZE )获得标题栏上按钮的水平宽度,

GetSystemMetrics(SM_CYSIZE)获得标题栏上按钮的竖直高度。用 CWnd 类的 IsZoomed()

函数判断是否为最大化还是恢复状态。双缓冲贴图用 CDC* pDisplayMemDC=new

CDC;pDisplayMemDC->CreateCompatibleDC(pDC);BitBlt()函数。

2、处理鼠标位于自绘按钮和自带按钮以及图标上的HitTest:在1中给m_rtButtExit;CRect

m_rtButtMax;CRect m_rtButtMin 和 m_rtIcon 赋值,记录相应按钮位置,在 OnNcHitTest

()

函数中用m_ct(point)判断鼠标是否位于自绘按钮区域,是则返回相应HitTest

值,同时判断鼠标是否位于系统自带的按钮上,是则当做鼠标位于标题栏,返回

HTCAPTION,屏蔽鼠标消息。

3、处理非客户区鼠标移动消息:在 OnNcMouseMove()函数中判断 nHitTest 值,得

到鼠标位于哪个按钮上,进行重绘鼠标指向时的自绘按钮。

4、 处理非客户区鼠标左击消息:在 OnNcLButtonDown()函数中判断 nHitTest 值,

得到鼠标位于哪个按钮上时按下,用 SendMessage(WM_CLOSE);

SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));等发

送相应消息。

5、处理非客户区双击消息:在 OnNcLButtonDblClk()函数中判断 nHitTest 值为

HTCAPTION 时,用 IsZoomed ()函数判断窗口是否为最大化,如果是则

SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));否则

SendMessage(WM_SYSCOMMAND,SC_MAXIMIZE,MAKELPARAM(point.x, point.y));

六、说明

1、如何去掉默认的主菜单:在 App 类中注释掉 BEGIN_MESSAGE_MAP 映射中的

行,注释掉 class CAboutDlg : public CDialog 后面所有内容。同时在 CMainFrm 中的

precreatewindow()中加上 =NULL;语句即可。注意一定不要完全删除资源中原有

的菜单。

2、修改标题:法一:在 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

函数里修改 me 值;法二:在 App 类的初始化函数里在显示并更新窗口前加上

AfxGetMainWnd()->SetWindowText(_T(“”));相应地如果设置其他标题用

//Set title for View’s MDI child frame window .

GetParentFrame ( ) —> SetWindowText ("_T ("MDI Child Frame new title"))

//Set title for dialog’s push button control.

GetDlgitem (IDC_BUTTON) —> SetWindowText (_T ("Button new title ") )

3、居中显示窗口:

法一:在 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) 函数里

cs.x=GetSystemMetrics(SM_CXSCREEN)//2;cs.y=GetSystemMetrics(SM_CYSCREEN)/

/2;

法二:(此法不好,会导致程序启动时闪动)在 App 类初始化函数中执行

AfxGetMainWnd()->CenterWindow();类似 CenterWindow()的实现代码可如下

//****居中显示

RECT rcDlg;

int cxDlg,cyDlg;

::GetWindowRect(hWnd,&rcDlg);

cxDlg=;

::cyDlg=;

SetWindowPos(hWnd,HWND_TOP,GetSystemMetrics(SM_CXSCREEN)/2-cxDlg/2,GetSystem

Metrics(SM_CYSCREEN)/2-cyDlg/2,0,0,SWP_NOSIZE);

4、程序运行顺序:从 App 类的App::InitInstance()函数开始运行到其中的

“// 调度在命令行中指定的命令。如果

// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。

if (!ProcessShellCommand(cmdInfo))

return FALSE; ”

进入if判断,先进入MainFrm中的PreCreateWindow(CREATESTRUCT& cs)执行后进入View

中的 PreCreateWindow(CREATESTRUCT& cs),执行后又回到 App 中继续往下执行。

5 、修改主窗口的位置和大小:不要用在 App 类初始化函数中执行

AfxGetMainWnd()->CenterWindow(GetDeskTopWindow());和 SetWindowPos()函数修改,

因为这样会在程序运行时闪动。好的方法是在 MainFrame 中的

PreCreateWindow(CREATESTRUCT& cs)中执行代码:

//设置窗口尺寸

=760;

=480;

//居中放置窗口

cs.x=GetSystemMetrics(SM_CXSCREEN)//2;

cs.y=GetSystemMetrics(SM_CYSCREEN)//2;

6、几个获取尺寸的函数:

GetClientRect()--------获得客户区坐标,相对于窗口左上角,不包括标题栏,所以 left 和 top

值始终是 0;

GetWindowRect()---------相对于屏幕左上角的坐标;

GetSystemMetrics()---------其中的 SM_CXFRAME 表示边框的水平厚度,SM_CXSIZE 表

示标题栏的宽度,也即关闭按钮的宽度。

7、strchblt()和 bitblt()双缓冲绘图的区别:前者可使缓冲区的图像大小随贴图区

小改变,后者不能。

8、最大化遮住任务栏、最大化后可移动窗口的解释:WS_THICKFRAME、

WS_CAPTION、WS_MAXIMIZEBOX 三个风格的作用。如果去掉这三个风格, &=

~WS_MAXIMIZEBOX; &=~WS_THICKFRAME; &=~WS_CAPTION;最大化

后就会遮住任务栏,同时窗口可移动。如果仅去掉 WS_MAXIMIZEBOX,最大化后不遮住

任务栏,但仍可移动窗口,如果不想让窗口移动,则需在 OnNcLButtonDown()函数中添

加 if(IsZoomed()&&nHitTest==HTCaption)nHitTest=HTNOWHERE;如果需要去掉

WS_MAXIMIZEBOX 和 WS_THICKFRAME、WS_CAPTION 同时最大化还不遮住任务

栏,

则需要在执行最大化前修改风格,用 ModifyStyle();最大化后再改过来。

9、绘制图标:用全局函数::DrawIconEx(pDC->m_hDC, m_, m_,

AfxGetApp()->LoadIcon(IDR_MAINFRAME),m_(), m_(), 0, NULL,

DI_NORMAL)。

10、让系统自带按钮存在的目的:只有这样系统才处理产生鼠标指向按钮时的 Tooltips,

这样自绘按钮利用系统已有功能显示 Tooltips。

由于水平所限,错误之处在所难免,欢迎各位读者批评指正。


本文标签: 按钮 鼠标 位于 自绘 函数