您的位置:首页 > 编程语言 > C语言/C++

MFC MDI和SDI非客户区框架界面美化之----标题栏以及边框美化

2016-11-03 10:55 447 查看
1、美化方式:贴图、重绘

优点:简单,易理解!适合

缺点:美化限制较大,实际使用意义不大。

2、基本思路是重载CMainFrame类的DefWindowProc()函数,并判断消息为:WM_NCPAINT,WM_NCACTIVATE,WM_NOTIFY的时候,调用自己的绘制窗口标题栏的函数。用GetSystemMetrics(SM_CSFRAME)和GetSystemMetrics(SM_CYFRAME)可以取得标题栏的左上角的坐标。最大化,最小化的按钮自己画,如果不是在标准的位置,一定要记录下他们的位置,并且在WM_NCLBUTTONDOWN消息处理函数中判断是否是点击了按钮,以做出相应的处理。系统图标也可以自己重新画。

主要任务有贴图(包括标题栏、左边界、右边界、下边界、系统图标、最大化、最小化、关闭按钮)、处理消息(屏蔽系统自带按钮、双击状态栏改变大小、鼠标停放在三个自绘按钮上时改变按钮图标、单击自绘按钮时作出相应反应)。

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

响应的消息及重载的函数都在CMainFrame类中。响应DefWindowProc函数,在其中判断消息是不是WM_NCPAINT、WM_MOVE、WM_NCACTIVATE、WM_NOTIFY,若是则重画标题栏、左框架、右框架、下框架、最大化、最小化、关闭按钮(放在一个函数里)。

响应消息WM_NCHITTEST,使鼠标位于自绘按钮时返回相应hittest值,同时屏蔽自带按钮的鼠标事件。简言之,当鼠标位于自绘按钮时,让系统误以为鼠标位于相应按钮,而当鼠标位于系统自带按钮时,让系统误以为鼠标只是位于标题栏。自绘图标与之类似,不再赘述。

响应消息WM_NCMOUSEMOVE,判断光标是不是位于自绘最大化、最小化、关闭按钮区域,如是则重画相应的按钮。

响应消息WM_NCLBUTTONDOWN,判断单击左键时鼠标是否位于自绘制的最大化、最小化、关闭按钮或图标区域,如是则执行相应的按钮操作。

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

[cpp]
view plain
copy

print?





LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // TODO: 在此添加专用代码和/或调用基类 if(message==WM_MOVE||message==WM_NCPAINT||message==WM_NCACTIVATE||message==WM_NOTIFY) { CDC *pWinDC=GetWindowDC(); if(pWinDC) DrawFrame(pWinDC); //自定义重绘函数 ReleaseDC(pWinDC); return TRUE; } else return CMDIFrameWndEx::DefWindowProc(message, wParam, lParam)



LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: 在此添加专用代码和/或调用基类
if(message==WM_MOVE||message==WM_NCPAINT||message==WM_NCACTIVATE||message==WM_NOTIFY)
{
CDC *pWinDC=GetWindowDC();
if(pWinDC)
DrawFrame(pWinDC); //自定义重绘函数
ReleaseDC(pWinDC);
return TRUE;
}
else
return CMDIFrameWndEx::DefWindowProc(message, wParam, lParam)


[cpp]
view plain
copy

print?





}



}


[cpp]
view plain
copy

print?





//此函数实现对框架的自绘
void CMainFrame::DrawFrame(CDC* pDC)
{
CDC* pDisplayMemDC=new CDC;
pDisplayMemDC->CreateCompatibleDC(pDC);
//分别用于保存窗口位置、按钮位置、框架位置和标题栏位置的临时变量
CRect rtWnd,rtButtons,rtTitle;
//获取窗口位置
GetWindowRect(&rtWnd);
//获取标题栏位置
rtTitle.top=GetSystemMetrics(SM_CYFRAME);
rtTitle.left=GetSystemMetrics(SM_CXFRAME);
rtTitle.bottom=GetSystemMetrics(SM_CYFRAME)+GetSystemMetrics(SM_CYSIZE);
rtTitle.right=rtWnd.Width()-GetSystemMetrics(SM_CXFRAME);
//准备贴图
CBitmap *pBitmap=new CBitmap;
BITMAP bm;
//重绘标题栏
pBitmap->LoadBitmap(IDB_TITLEBAR);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
pDC->StretchBlt(0,0,rtWnd.Width(),rtTitle.Height()+GetSystemMetrics(SM_CYFRAME)+2,pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pBitmap->DeleteObject();
//重绘左边框右边框和下边框
pBitmap->LoadBitmap(IDB_LEFTANDRIGHT);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
pDC->StretchBlt(0,0,GetSystemMetrics(SM_CXFRAME)+1,rtWnd.Height(),pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pDC->StretchBlt(rtWnd.Width()-GetSystemMetrics(SM_CXFRAME),0,rtWnd.Width(),rtWnd.Height(),pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pBitmap->DeleteObject();
pBitmap->LoadBitmap(IDB_BOTTOM);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
pDC->StretchBlt(0,rtWnd.Height()-GetSystemMetrics(SM_CYFRAME),rtWnd.Width(),rtWnd.Height(),pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘关闭按钮
pBitmap->LoadBitmap(IDB_EXIT_NORMAL);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
//设置关闭按钮位置
rtButtons.left=rtTitle.right-bm.bmWidth-5;
rtButtons.top=GetSystemMetrics(SM_CYFRAME)+(rtTitle.Height()-bm.bmHeight)/2;
rtButtons.right=rtButtons.left+bm.bmWidth;
rtButtons.bottom=rtButtons.top+bm.bmHeight;
m_rtButtExit=rtButtons;
//重绘关闭按钮
pDC->BitBlt(rtButtons.left,rtButtons.top,rtButtons.Width(),rtButtons.Height(),pDisplayMemDC,0,0,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘最大化/恢复按钮
if(IsZoomed())
pBitmap->LoadBitmap(IDB_RESTORE_NORMAL);
else
pBitmap->LoadBitmap(IDB_MAX_NORMAL);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
//设置最大化/恢复按钮位置
rtButtons.right=m_rtButtExit.left-2;
rtButtons.left=rtButtons.right-bm.bmWidth;
rtButtons.top=m_rtButtExit.top;
rtButtons.bottom=m_rtButtExit.bottom;
m_rtButtMax=rtButtons;
//重绘最大化/恢复按钮
pDC->BitBlt(rtButtons.left,rtButtons.top,rtButtons.Width(),rtButtons.Height(),pDisplayMemDC,0,0,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘最小化按钮
pBitmap->LoadBitmap(IDB_MIN_NORMAL);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
//设置最小化按钮位置
rtButtons.right=m_rtButtMax.left-2;
rtButtons.left=rtButtons.right-bm.bmWidth;
rtButtons.top=m_rtButtMax.top;
rtButtons.bottom=m_rtButtMax.bottom;
m_rtButtMin=rtButtons;
//重绘最小化按钮
pDC->BitBlt(rtButtons.left,rtButtons.top,rtButtons.Width(),rtButtons.Height(),pDisplayMemDC,0,0,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘图标
m_rtIcon.left=GetSystemMetrics(SM_CXFRAME);
m_rtIcon.top=GetSystemMetrics(SM_CYFRAME);
m_rtIcon.right=m_rtIcon.left+GetSystemMetrics(SM_CXSIZE);
m_rtIcon.bottom=m_rtIcon.top+GetSystemMetrics(SM_CYSIZE);
::DrawIconEx(pDC->m_hDC, m_rtIcon.left, m_rtIcon.top, AfxGetApp()->LoadIcon(IDR_MAINFRAME),
m_rtIcon.Width(), m_rtIcon.Height(), 0, NULL, DI_NORMAL);

//绘制标题
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(_T("美化框架"),CRect(m_rtIcon.right+4,GetSystemMetrics(SM_CYFRAME)+4,400,GetSystemMetrics(SM_CYSIZE)+60),DT_NOCLIP|DT_SINGLELINE);

ReleaseDC(pDisplayMemDC);
delete pDisplayMemDC;
delete pBitmap;
}



//此函数实现对框架的自绘
void CMainFrame::DrawFrame(CDC* pDC)
{
CDC* pDisplayMemDC=new CDC;
pDisplayMemDC->CreateCompatibleDC(pDC);
//分别用于保存窗口位置、按钮位置、框架位置和标题栏位置的临时变量
CRect rtWnd,rtButtons,rtTitle;
//获取窗口位置
GetWindowRect(&rtWnd);
//获取标题栏位置
rtTitle.top=GetSystemMetrics(SM_CYFRAME);
rtTitle.left=GetSystemMetrics(SM_CXFRAME);
rtTitle.bottom=GetSystemMetrics(SM_CYFRAME)+GetSystemMetrics(SM_CYSIZE);
rtTitle.right=rtWnd.Width()-GetSystemMetrics(SM_CXFRAME);
//准备贴图
CBitmap *pBitmap=new CBitmap;
BITMAP bm;
//重绘标题栏
pBitmap->LoadBitmap(IDB_TITLEBAR);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
pDC->StretchBlt(0,0,rtWnd.Width(),rtTitle.Height()+GetSystemMetrics(SM_CYFRAME)+2,pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pBitmap->DeleteObject();
//重绘左边框右边框和下边框
pBitmap->LoadBitmap(IDB_LEFTANDRIGHT);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
pDC->StretchBlt(0,0,GetSystemMetrics(SM_CXFRAME)+1,rtWnd.Height(),pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pDC->StretchBlt(rtWnd.Width()-GetSystemMetrics(SM_CXFRAME),0,rtWnd.Width(),rtWnd.Height(),pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pBitmap->DeleteObject();
pBitmap->LoadBitmap(IDB_BOTTOM);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
pDC->StretchBlt(0,rtWnd.Height()-GetSystemMetrics(SM_CYFRAME),rtWnd.Width(),rtWnd.Height(),pDisplayMemDC,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘关闭按钮
pBitmap->LoadBitmap(IDB_EXIT_NORMAL);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
//设置关闭按钮位置
rtButtons.left=rtTitle.right-bm.bmWidth-5;
rtButtons.top=GetSystemMetrics(SM_CYFRAME)+(rtTitle.Height()-bm.bmHeight)/2;
rtButtons.right=rtButtons.left+bm.bmWidth;
rtButtons.bottom=rtButtons.top+bm.bmHeight;
m_rtButtExit=rtButtons;
//重绘关闭按钮
pDC->BitBlt(rtButtons.left,rtButtons.top,rtButtons.Width(),rtButtons.Height(),pDisplayMemDC,0,0,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘最大化/恢复按钮
if(IsZoomed())
pBitmap->LoadBitmap(IDB_RESTORE_NORMAL);
else
pBitmap->LoadBitmap(IDB_MAX_NORMAL);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
//设置最大化/恢复按钮位置
rtButtons.right=m_rtButtExit.left-2;
rtButtons.left=rtButtons.right-bm.bmWidth;
rtButtons.top=m_rtButtExit.top;
rtButtons.bottom=m_rtButtExit.bottom;
m_rtButtMax=rtButtons;
//重绘最大化/恢复按钮
pDC->BitBlt(rtButtons.left,rtButtons.top,rtButtons.Width(),rtButtons.Height(),pDisplayMemDC,0,0,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘最小化按钮
pBitmap->LoadBitmap(IDB_MIN_NORMAL);
pBitmap->GetBitmap(&bm);
pDisplayMemDC->SelectObject(pBitmap);
//设置最小化按钮位置
rtButtons.right=m_rtButtMax.left-2;
rtButtons.left=rtButtons.right-bm.bmWidth;
rtButtons.top=m_rtButtMax.top;
rtButtons.bottom=m_rtButtMax.bottom;
m_rtButtMin=rtButtons;
//重绘最小化按钮
pDC->BitBlt(rtButtons.left,rtButtons.top,rtButtons.Width(),rtButtons.Height(),pDisplayMemDC,0,0,SRCCOPY);
pBitmap->DeleteObject();

//准备重绘图标
m_rtIcon.left=GetSystemMetrics(SM_CXFRAME);
m_rtIcon.top=GetSystemMetrics(SM_CYFRAME);
m_rtIcon.right=m_rtIcon.left+GetSystemMetrics(SM_CXSIZE);
m_rtIcon.bottom=m_rtIcon.top+GetSystemMetrics(SM_CYSIZE);
::DrawIconEx(pDC->m_hDC, m_rtIcon.left, m_rtIcon.top, AfxGetApp()->LoadIcon(IDR_MAINFRAME),
m_rtIcon.Width(), m_rtIcon.Height(), 0, NULL, DI_NORMAL);

//绘制标题
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(_T("美化框架"),CRect(m_rtIcon.right+4,GetSystemMetrics(SM_CYFRAME)+4,400,GetSystemMetrics(SM_CYSIZE)+60),DT_NOCLIP|DT_SINGLELINE);

ReleaseDC(pDisplayMemDC);
delete pDisplayMemDC;
delete pBitmap;
}


其他函数源代码见DEMO。

4、说明

1、如何去掉默认的主菜单:

在App类中注释掉BEGIN_MESSAGE_MAP映射中的三行,注释掉class CAboutDlg : public CDialog后面所有内容。同时在CMainFrm中的precreatewindow()中加上cs.hMenu=NULL;语句即可。注意一定不要完全删除资源中原有的菜单。

2、修改标题:

法一:在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数里修改cs.lpszName值;法二:在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.cx/2;

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

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

类似CenterWindow()的实现代码可如下

//****居中显示

RECT rcDlg;

int cxDlg,cyDlg;

::GetWindowRect(hWnd,&rcDlg);

cxDlg=rcDlg.right-rcDlg.left;

::cyDlg=rcDlg.bottom-rcDlg.top;

SetWindowPos(hWnd,HWND_TOP,GetSystemMetrics(SM_CXSCREEN)/2-cxDlg/2,GetSystemMetrics(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)中执行代码 //设置窗口尺寸

cs.cx=760;

cs.cy=480;

//居中放置窗口

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

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

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

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

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

GetSystemMetrics()---------其中的SM_CXFRAME表示边框的水平厚度,SM_CXSIZE表示标题栏的宽度,也即关闭按钮的宽度。

7、strchblt()和bitblt()双缓冲绘图的区别:

前者可使缓冲区的图像大小随贴图区大小改变,后者不能。

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

WS_THICKFRAME、WS_CAPTION、WS_MAXIMIZEBOX三个风格的作用。如果去掉这三个风格,cs.style &= ~WS_MAXIMIZEBOX;cs.style &=~WS_THICKFRAME;cs.style &=~WS_CAPTION;最大化后就会遮住任务栏,同时窗口可移动。如果仅去掉WS_MAXIMIZEBOX,最大化后不遮住任务栏,但仍可移动窗口,如果不想让窗口移动,则需在OnNcLButtonDown()函数中添加if(IsZoomed()&&nHitTest==HTCaption)nHitTest=HTNOWHERE;如果需要去掉WS_MAXIMIZEBOX和WS_THICKFRAME、WS_CAPTION同时最大化还不遮住任务栏,则需要在执行最大化前修改风格,用ModifyStyle();最大化后再改过来。

9、绘制图标:

用全局函数::DrawIconEx(pDC->m_hDC, m_rtIcon.left, m_rtIcon.top, AfxGetApp()->LoadIcon(IDR_MAINFRAME),

m_rtIcon.Width(), m_rtIcon.Height(), 0, NULL, DI_NORMAL);

10、让系统自带按钮存在的目的:

只有这样系统才处理产生鼠标指向按钮时的Tooltips,这样自绘按钮利用系统已有功能显示Tooltips,

11、运行效果



DEMO下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ MFC 界面