本程序在VC03测试成功,效果, 图片素材:从BC1.bmp到第2页的UR_N.bmp
1.准备工作:
(1)得到文件夹中的位图句柄:
首先要准备相应图片。
复制代码
其中,skin//Test.bmp为文件路径。
(2)关于非客户区的消息:
复制代码
(3)改变标题栏尺寸:
重写ON_WM_NCCALCSIZE()消息响应函数。
复制代码
(4)非客户区的鼠标动作:
相关消息:
复制代码
(5)屏蔽最大最小关闭消息:
在WindowProc中:
复制代码
2.程序和注释:
(1)用户变量和函数.
复制代码
(2)显示之前计算从图片计算标题栏高度:
复制代码
(3)重写OnNcCalCsize:
复制代码
(4)绘非客户区:
复制代码
(5)对非客户区焦点情况的处理:
复制代码
(6)响应鼠标在非客户区的事件:
鼠标在非客户区按下:
复制代码
(7)屏蔽单击程序非客户系统原有图标矩形时出现的系统菜单或动作:
复制代码
(8)系统菜单的显示和隐藏:
为了使重绘工作顺利进行而不影响程序外在表现,要对系统菜单显示和隐藏, 如在OnNcActivate中有这样的程序片段:
复制代码
又如,在OnCreate中:
复制代码
(9)对系统最大最小关闭图标依然出现的处理:
虽然用户取消了NC区系统的重绘,但是系统仍然对最大最小关闭图标重绘(主要表现在用户右单击任务栏图标时),这里的处理方法如下:
复制代码
3.源程序:
(1)MYSkinDlg.h:
复制代码
(2)MYSkinDlg.cpp:
复制代码
4.改进
不足之处:
(1)只针对对话框。
(2)图片重绘时的闪烁问题。
(3)图片装载时的错误处理。
(4)最大最小关闭图标依然显示的问题。
(5)border宽度不能随窗口样式改变的问题。
(6)没有解决最大最小的不使能,显/隐的问题。
(7)XP环境下的最佳性能时标题栏左上和右上角多余区域的处理。
(8)程序有待进一步简化和优化。
==================================
5.2007.9.10更新(同时祝天下所有的老师教师节快乐!)
彻底解决最大最小关闭按钮依然显示的问题:
在WindowProc中加入:
复制代码
去掉所有ModifyStyle(0, WS_SYSMENU);和ModifyStyle(WS_SYSMENU, 0);的程序片段和相关语句.
1.准备工作:
(1)得到文件夹中的位图句柄:
首先要准备相应图片。
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//Test.bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- CBitmap cbmp;
- cbmp.Attach(bitmap);
(2)关于非客户区的消息:
- ON_WM_NCPAINT()//绘非客户区时。
- ON_WM_NCACTIVATE()//非客户区有焦点和失去焦点时。
- ON_WM_NCCALCSIZE()//计算窗体尺寸时。
重写ON_WM_NCCALCSIZE()消息响应函数。
- void CMYSkinDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
- {
- //重设标题栏高度。m_nCaptionHeight在PreSubclassWindow中计算。
- lpncsp->rgrc[0].top+=m_nCaptionHeight-GetSystemMetrics(SM_CYCAPTION);
- CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
- }
相关消息:
- ON_WM_NCLBUTTONDOWN()//鼠标下。
- ON_WM_NCLBUTTONUP()//鼠标上。
- ON_WM_NCMOUSEMOVE()//鼠标悬停。
在WindowProc中:
- LRESULT CMYSkinDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
- {
- if (message == WM_NCHITTEST)
- {
- LRESULT lRet = CDialog::WindowProc(message, wParam, lParam);
- //屏蔽最大最小关闭消息.
- if (lRet==HTZOOM || lRet == HTMINBUTTON || lRet == HTCLOSE)
- return HTCAPTION;//视为标题栏动作。
- else
- return lRet;
- }
- }
(1)用户变量和函数.
- protected:
- CBrush m_brBG;//对话框背景颜色,在OnInitDialog 中初始化,在OnCtlColor中作为返回值.
- CString m_strCaption;//标题.
- CRect m_rtWnd;//整个窗体Rect.
- int m_nCaptionHeight;//标题栏高度.
- CRect m_rtButtons;//最大,最小,关闭按钮.
- CRect m_rtIcon;//图标.
- CRect m_rtButtMin;//最小.
- CRect m_rtButtMax;//最大.
- CRect m_rtButtExit;//关闭.
- CRect m_rtButtMaxM;
- CRect m_rtButtMinM;
- CRect m_rtButtExitM;
- CRect m_bmRt;//Bitmap所在的Rect.
- BOOL m_bNCActive;//窗体活动.
- bool FillRtWithBmp(CString bmpFileName,CDC *pDC,CRect rt);//用Bitmap添满整个rt.
- bool FillWithBmpRtUL(CString bmpFileName,CDC *pDC,CPoint pt);//pt为Bitmap的左上.
- bool FillWithBmpRtUR(CString bmpFileName,CDC *pDC, CPoint pt);//pt为Bitmap的右上.
- bool FillButton(CString bmpButtonName, CDC *pDC, CPoint pt,int intState);//
- void DrawNC(CDC* pDC);//画非客户区.
- void CMYSkinDlg::PreSubclassWindow()
- {
- //得到标题栏图片高度。
- CString bmpFileName="C2";
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//"+_T(bmpFileName)+".bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- BITMAP bm;
- CBitmap cbmp;
- cbmp.Attach(bitmap);
- cbmp.GetBitmap(&bm);
- cbmp.DeleteObject();
- m_nCaptionHeight=bm.bmHeight-4;
- CDialog::PreSubclassWindow();
- }
- void CMYSkinDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
- {
- //重设标题栏高度。m_nCaptionHeight在PreSubclassWindow中计算。
- lpncsp->rgrc[0].top+=m_nCaptionHeight-GetSystemMetrics(SM_CYCAPTION);
- CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
- }
- void CMYSkinDlg::OnNcPaint()
- {
- CDC* pWinDC=GetWindowDC();
- if (pWinDC) DrawNC(pWinDC);//函数实现略。
- ReleaseDC(pWinDC);
- }
- BOOL CMYSkinDlg::OnNcActivate(BOOL bActive)
- {
- m_bNCActive=bActive;//在DrawNC中有体现。//初始时设NC区为活动.
- //防止在任务栏右键图标时出现最大最小关闭
- OnNcPaint();//实际源程序中有细节的考虑。
- return true;
- }
鼠标在非客户区按下:
- void CMYSkinDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
- {
- //检测最小,最大和关闭按钮是否按下,然后更换图片.
- if(!IsZoomed())//防止在最大化后能拖动.
- CDialog::OnNcLButtonDown(nHitTest,point);
- }
- //OnNcLButtonUp中触发最大最小关闭:
- void CMYSkinDlg::OnNcLButtonUp(UINT nHitTest, CPoint point)
- {
- if (m_rtButtExit.PtInRect(point))
- SendMessage(WM_CLOSE);
- else if (m_rtButtMin.PtInRect(point))
- SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM(point.x, point.y) );
- else if (m_rtButtMax.PtInRect(point))
- {
- if (IsZoomed())
- SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));
- else
- SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM(point.x, point.y) );
- }
- }
- //鼠标在非客户区悬停:
- void CMYSkinDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
- {
- //检测最小,最大和关闭按钮是否有鼠标悬停,然后更换相应图片.
- }
- UINT CMYSkinDlg::OnNcHitTest(CPoint point)
- {
- CRect tst(2,2,m_nCaptionHeight+4,m_nCaptionHeight+4);
- tst.OffsetRect(m_rtWnd.TopLeft());//原图标屏幕位置
- if(tst.PtInRect(point))//最大最小关闭按钮位置.
- return HTCAPTION;
- else if(m_rtButtMin.PtInRect(point)||
- m_rtButtMax.PtInRect(point)||
- m_rtButtExit.PtInRect(point))
- return HTSYSMENU;//使此区域能够响应OnNcLButtonUp
- else
- return CDialog::OnNcHitTest(point);
- }
为了使重绘工作顺利进行而不影响程序外在表现,要对系统菜单显示和隐藏, 如在OnNcActivate中有这样的程序片段:
- if(bActive)
- {
- ModifyStyle(0, WS_SYSMENU);
- OnNcPaint();
- }
- else
- {
- ModifyStyle(WS_SYSMENU, 0);
- OnNcPaint();
- }
- this->ModifyStyle(WS_SYSMENU, 0);//防止在任务栏右单击时出现最大最小关闭.
虽然用户取消了NC区系统的重绘,但是系统仍然对最大最小关闭图标重绘(主要表现在用户右单击任务栏图标时),这里的处理方法如下:
- void CMYSkinDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
- {
- //CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
- if (bSysMenu) OnNcPaint();//防止在任务栏右键图标时窗口出现最大最小关闭
- }
(1)MYSkinDlg.h:
- 实现
- protected://用户变量和函数.
- CBrush m_brBG;//对话框背景颜色,在OnInitDialog 中初始化,在OnCtlColor中作为返回值.
- CString m_strCaption;//标题.
- CRect m_rtWnd;//整个窗体Rect.
- int m_nCaptionHeight;//标题栏高度.
- CRect m_rtButtons;//最大,最小,关闭按钮.
- CRect m_rtIcon;//图标.
- CRect m_rtButtMin;//最小.
- CRect m_rtButtMax;//最大.
- CRect m_rtButtExit;//关闭.
- CRect m_rtButtMaxM;
- CRect m_rtButtMinM;
- CRect m_rtButtExitM;
- CRect m_bmRt;//Bitmap所在的Rect.
- BOOL m_bNCActive;//窗体活动.
- bool FillRtWithBmp(CString bmpFileName,CDC *pDC,CRect rt);//用Bitmap添满整个rt.
- bool FillWithBmpRtUL(CString bmpFileName,CDC *pDC,CPoint pt);//pt为Bitmap的左上.
- bool FillWithBmpRtUR(CString bmpFileName,CDC *pDC, CPoint pt);//pt为Bitmap的右上.
- bool FillButton(CString bmpButtonName, CDC *pDC, CPoint pt,int intState);//
- void DrawNC(CDC* pDC);//画非客户区.
- // MYSkinDlg.cpp : 实现文件
- #include "stdafx.h"
- #include "MYSkin.h"
- #include "MYSkinDlg.h"
- #include ".\myskindlg.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- // CMYSkinDlg 对话框
- CMYSkinDlg::CMYSkinDlg(CWnd* pParent /*=NULL*/)
- : CDialog(CMYSkinDlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- }
- void CMYSkinDlg::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
- }
- BEGIN_MESSAGE_MAP(CMYSkinDlg, CDialog)
- ON_WM_PAINT()
- ON_WM_QUERYDRAGICON()
- //}}AFX_MSG_MAP
- ON_BN_CLICKED(IDC_BUTTON, OnBnClickedButton)
- ON_WM_NCPAINT()
- ON_WM_NCACTIVATE()
- ON_WM_NCCALCSIZE()
- ON_WM_NCHITTEST()
- ON_WM_NCLBUTTONDOWN()
- ON_WM_NCLBUTTONUP()
- ON_WM_MOVE()
- ON_WM_CREATE()
- ON_WM_INITMENUPOPUP()
- ON_WM_NCMOUSEMOVE()
- ON_WM_CTLCOLOR()
- END_MESSAGE_MAP()
- // CMYSkinDlg 消息处理程序
- BOOL CMYSkinDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
- // 执行此操作
- SetIcon(m_hIcon, TRUE); // 设置大图标
- SetIcon(m_hIcon, FALSE); // 设置小图标
- // TODO: 在此添加额外的初始化代码
- m_bNCActive=true;//初始NC区为活动.
- m_brBG.CreateSolidBrush(RGB(220, 220, 220)); //对话框背景颜色,在OnCtlColor中作为返回值.
- return TRUE; // 除非设置了控件的焦点,否则返回 TRUE
- }
- // 如果向对话框添加最小化按钮,则需要下面的代码
- // 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
- // 这将由框架自动完成。
- void CMYSkinDlg::OnPaint()
- {
- if (IsIconic())
- {
- CPaintDC dc(this); // 用于绘制的设备上下文
- SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
- // 使图标在工作矩形中居中
- int cxIcon = GetSystemMetrics(SM_CXICON);
- int cyIcon = GetSystemMetrics(SM_CYICON);
- CRect rect;
- GetClientRect(&rect);
- int x = (rect.Width() - cxIcon + 1) / 2;
- int y = (rect.Height() - cyIcon + 1) / 2;
- // 绘制图标
- dc.DrawIcon(x, y, m_hIcon);
- }
- else
- {
- CDialog::OnPaint();
- }
- }
- //当用户拖动最小化窗口时系统调用此函数取得光标显示。
- HCURSOR CMYSkinDlg::OnQueryDragIcon()
- {
- return static_cast<HCURSOR>(m_hIcon);
- }
- void CMYSkinDlg::OnBnClickedButton()
- {
- MessageBox("Main Dlg Lost Focus!!");
- }
- /
- void CMYSkinDlg::OnNcPaint()
- {
- CDC* pWinDC=GetWindowDC();
- if (pWinDC) DrawNC(pWinDC);
- ReleaseDC(pWinDC);
- }
- BOOL CMYSkinDlg::OnNcActivate(BOOL bActive)
- {
- m_bNCActive=bActive;
- //防止在任务栏右键图标时出现最大最小关闭
- if(bActive)
- {
- ModifyStyle(0, WS_SYSMENU);
- OnNcPaint();
- }
- else
- {
- ModifyStyle(WS_SYSMENU, 0);
- OnNcPaint();
- }
- return true;
- }
- void CMYSkinDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
- {
- //重设标题栏高度。m_nCaptionHeight在PreSubclassWindow中计算。
- lpncsp->rgrc[0].top+=m_nCaptionHeight-GetSystemMetrics(SM_CYCAPTION);
- CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
- }
- UINT CMYSkinDlg::OnNcHitTest(CPoint point)
- {
- CRect tst(2,2,m_nCaptionHeight+4,m_nCaptionHeight+4);
- tst.OffsetRect(m_rtWnd.TopLeft());//原图标屏幕位置
- if(tst.PtInRect(point))//最大最小关闭按钮位置.
- return HTCAPTION;
- else if(m_rtButtMin.PtInRect(point)||
- m_rtButtMax.PtInRect(point)||
- m_rtButtExit.PtInRect(point))
- return HTSYSMENU;//使此区域能够响应OnNcLButtonUp
- else
- return CDialog::OnNcHitTest(point);
- }
- void CMYSkinDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
- {
- //检测最小,最大和关闭按钮是否按下,然后更换.
- if (m_rtButtExit.PtInRect(point))
- { //绘关闭按钮按下时的图标
- CDC* pWinDC=GetWindowDC();
- FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),3);
- ReleaseDC(pWinDC);
- }
- else if (m_rtButtMin.PtInRect(point))
- {
- //绘最小化按钮按下时的图标
- CDC* pWinDC=GetWindowDC();
- FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),3);
- ReleaseDC(pWinDC);
- }
- else if (m_rtButtMax.PtInRect(point))
- { //绘最大化按钮按下时的图标
- CDC* pWinDC=GetWindowDC();
- if (IsZoomed())
- FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),3);
- else
- FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),3);
- ReleaseDC(pWinDC);
- }
- if(!IsZoomed())//防止在最大化后能拖动.
- CDialog::OnNcLButtonDown(nHitTest,point);
- }
- void CMYSkinDlg::OnNcLButtonUp(UINT nHitTest, CPoint point)
- {
- if (m_rtButtExit.PtInRect(point))
- SendMessage(WM_CLOSE);
- else if (m_rtButtMin.PtInRect(point))
- SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM(point.x, point.y) );
- else if (m_rtButtMax.PtInRect(point))
- {
- if (IsZoomed())
- SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));
- else
- SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM(point.x, point.y) );
- }
- }
- void CMYSkinDlg::OnMove(int x, int y)
- {
- CDialog::OnMove(x, y);
- //OnNcPaint();//减少闪烁,不用OnNcPaint()。
- //整个Window的相对于屏幕的矩形
- GetWindowRect(&m_rtWnd); //更新窗体矩形.
- m_rtButtMinM.OffsetRect(m_rtWnd.TopLeft()); //记录最小button屏幕位置
- m_rtButtMin=m_rtButtMinM;//记录最小button屏幕位置
- m_rtButtMinM.OffsetRect(-m_rtWnd.TopLeft());//还原.
- m_rtButtMaxM.OffsetRect(m_rtWnd.TopLeft());//记录button屏幕位置
- m_rtButtMax=m_rtButtMaxM;//记录button屏幕位置
- m_rtButtMaxM.OffsetRect(-m_rtWnd.TopLeft());//还原.
- m_rtButtExitM.OffsetRect(m_rtWnd.TopLeft());//记录关闭button屏幕位置
- m_rtButtExit=m_rtButtExitM;//记录关闭button屏幕位置
- m_rtButtExitM.OffsetRect(-m_rtWnd.TopLeft());//还原.
- }
- Bitmap Load Function//
- bool CMYSkinDlg::FillRtWithBmp(CString bmpFileName,CDC *pDC,CRect rt)
- {
- //填满整个矩形
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//"+_T(bmpFileName)+".bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- BITMAP bm;
- CBitmap cbmp;
- CDC MemDC;
- cbmp.Attach(bitmap);
- cbmp.GetBitmap(&bm);
- MemDC.CreateCompatibleDC (pDC);
- CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);
- CRect rttmp;
- rttmp=rt;
- int i=0;
- int j=0;
- if(rt.Width()/bm.bmWidth<1 && rt.Height()/bm.bmHeight<1)
- pDC->BitBlt(rt.left,rt.top,rt.Width(),rt.Height(),&MemDC,0,0,SRCCOPY);
- else if(rt.Width()/bm.bmWidth<1 && rt.Height()/bm.bmHeight>1)
- for (j=0; j<=rt.Height()/bm.bmHeight;j++)
- {
- pDC->BitBlt(rt.left,rttmp.top,rt.Width(),bm.bmHeight,&MemDC,0,0,SRCCOPY);
- rttmp.top+=bm.bmHeight;
- }
- else if(rt.Width()/bm.bmWidth>1 && rt.Height()/bm.bmHeight<1)
- for (i=0;i<=rt.Width()/bm.bmWidth;i++)
- {
- pDC->BitBlt(rttmp.left,rt.top,bm.bmWidth,rt.Height(),&MemDC,0,0,SRCCOPY);
- rttmp.left+=bm.bmWidth;
- }
- else
- {
- for (i=0;i<rt.Width()/bm.bmWidth;i++)
- {
- for (j=0; j<rt.Height()/bm.bmHeight;j++)
- {
- pDC->BitBlt(rttmp.left,rttmp.top,bm.bmWidth,bm.bmHeight,&MemDC,0,0,SRCCOPY);
- rttmp.top+=bm.bmHeight;
- }
- rttmp.top=rt.top;
- rttmp.left+=bm.bmWidth;
- }
- rttmp=rt;
- for (i=0;i<rt.Width()/bm.bmWidth;i++)
- {
- pDC->BitBlt(rttmp.left,
- rt.top+rt.Height()-(rt.Height()%bm.bmHeight),
- bm.bmWidth,
- rt.Height()%bm.bmHeight,
- &MemDC,0,0,SRCCOPY);
- rttmp.left+=bm.bmWidth;
- }
- rttmp=rt;
- for (j=0;j<rt.Height()/bm.bmHeight;j++)
- {
- pDC->BitBlt(rt.left+rt.Width()-(rt.Width()%bm.bmWidth),
- rttmp.top,
- rt.Width()%bm.bmWidth,
- bm.bmHeight,
- &MemDC,0,0,SRCCOPY);
- rttmp.top+=bm.bmHeight;
- }
- rttmp=rt;
- pDC->BitBlt(rt.left+rt.Width()-(rt.Width()%bm.bmWidth),
- rt.top+rt.Height()-(rt.Height()%bm.bmHeight),
- rt.Width()%bm.bmWidth,
- rt.Height()%bm.bmHeight,
- &MemDC,0,0,SRCCOPY);
- }
- MemDC.SelectObject (pOldBitmap);
- ReleaseDC(&MemDC);
- cbmp.DeleteObject();
- pOldBitmap->DeleteObject();
- return true;
- }
- bool CMYSkinDlg::FillWithBmpRtUL(CString bmpFileName,CDC *pDC, CPoint pt)
- {
- //只填一张Bitmap.参考点pt为图片左上.
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//"+_T(bmpFileName)+".bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- BITMAP bm;
- CBitmap cbmp;
- cbmp.Attach(bitmap);
- cbmp.GetBitmap(&bm);
- m_bmRt.left=pt.x;//m_bmRt为全局.
- m_bmRt.top=pt.y;
- m_bmRt.right=pt.x + bm.bmWidth;
- m_bmRt.bottom=pt.y + bm.bmHeight;
- CDC MemDC;
- MemDC.CreateCompatibleDC (pDC);
- CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);
- pDC->BitBlt(pt.x,pt.y,pt.x+bm.bmWidth ,pt.y+bm.bmHeight ,&MemDC,0,0,SRCCOPY);
- MemDC.SelectObject (pOldBitmap);
- ReleaseDC(&MemDC);
- cbmp.DeleteObject();
- pOldBitmap->DeleteObject();
- return true;
- }
- bool CMYSkinDlg::FillWithBmpRtUR(CString bmpFileName,CDC *pDC, CPoint pt)
- {
- //只填一张图,参考点pt为右上.
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//"+_T(bmpFileName)+".bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- BITMAP bm;
- CBitmap cbmp;
- cbmp.Attach(bitmap);
- cbmp.GetBitmap(&bm);
- m_bmRt.left=pt.x - bm.bmWidth;//m_bmRt为全局.
- m_bmRt.top=pt.y;
- m_bmRt.right=pt.x;
- m_bmRt.bottom=pt.y + bm.bmHeight;
- CDC MemDC;
- MemDC.CreateCompatibleDC (pDC);
- CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);
- pDC->BitBlt(pt.x-bm.bmWidth,pt.y,pt.x,pt.y+bm.bmHeight ,&MemDC,0,0,SRCCOPY);
- MemDC.SelectObject (pOldBitmap);
- ReleaseDC(&MemDC);
- cbmp.DeleteObject();
- pOldBitmap->DeleteObject();
- return true;
- }
- bool CMYSkinDlg::FillButton(CString bmpButtonName, CDC *pDC, CPoint pt,int intState)
- {
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//"+_T(bmpButtonName)+".bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- BITMAP bm;
- CBitmap cbmp;
- cbmp.Attach(bitmap);
- cbmp.GetBitmap(&bm);
- CDC MemDC;
- MemDC.CreateCompatibleDC (pDC);
- CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);
- switch(intState)
- {
- case 1:
- pDC->BitBlt(pt.x, pt.y, bm.bmWidth / 3, bm.bmHeight,&MemDC,
- 0,0,SRCCOPY);
- break;
- case 2:
- pDC->BitBlt(pt.x, pt.y, bm.bmWidth / 3, bm.bmHeight,&MemDC,
- bm.bmWidth / 3, 0, SRCCOPY);
- break;
- case 3:
- pDC->BitBlt(pt.x, pt.y, bm.bmWidth / 3, bm.bmHeight,&MemDC,
- bm.bmWidth * 2 / 3, 0, SRCCOPY);
- break;
- }
- m_bmRt.left = pt.x;//m_bmRt为全局.
- m_bmRt.top = pt.y;
- m_bmRt.right = pt.x + bm.bmWidth / 3;
- m_bmRt.bottom = pt.y + bm.bmHeight;
- MemDC.SelectObject (pOldBitmap);
- ReleaseDC(&MemDC);
- cbmp.DeleteObject();
- pOldBitmap->DeleteObject();
- return true;
- }
- Bitmap Load Function end///
- Draw NC
- void CMYSkinDlg::DrawNC(CDC* pDC)
- {
- if (m_hWnd)
- {
- CRect rtTitle;//
- //整个Window的相对于屏幕的矩形
- GetWindowRect(&m_rtWnd);
- //取得整个Title bar的矩形
- rtTitle.left=0;
- rtTitle.top=0;
- rtTitle.right=m_rtWnd.Width()+3;
- rtTitle.bottom=rtTitle.top+m_nCaptionHeight + 4;
- //重画Title Bar
- if (m_bNCActive)
- {
- FillWithBmpRtUL("UL",pDC,CPoint(0,0));
- CRect rtUL=m_bmRt;
- FillWithBmpRtUR("C2",pDC,CPoint(rtTitle.right-3,rtTitle.top));
- FillRtWithBmp("C1",pDC,CRect(rtTitle.left+rtUL.Width(),rtTitle.top,rtTitle.right-m_bmRt.Width()-3,rtTitle.bottom));
- }
- else
- {
- FillWithBmpRtUL("UL_N",pDC,CPoint(0,0));
- CRect rtUL=m_bmRt;
- FillWithBmpRtUR("C2_N",pDC,CPoint(rtTitle.right-3,rtTitle.top));
- FillRtWithBmp("C1_N",pDC,CRect(rtTitle.left+rtUL.Width(),rtTitle.top,rtTitle.right-m_bmRt.Width()-3,rtTitle.bottom));
- }
- //重画icon
- HICON hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- m_rtIcon.left=rtTitle.left+15;
- m_rtIcon.top=rtTitle.top+2;
- m_rtIcon.right=m_rtIcon.left+rtTitle.Height()-5;
- m_rtIcon.bottom=m_rtIcon.top+rtTitle.Height()-5;
- ::DrawIconEx(pDC->m_hDC,
- m_rtIcon.left, m_rtIcon.top,
- hIcon,
- m_rtIcon.Height(), m_rtIcon.Width(),
- 0, NULL,DI_NORMAL);
- m_rtIcon.OffsetRect(m_rtWnd.TopLeft()); //记录Icon屏幕位置
- //重画最大最小关闭button
- //MIN
- FillButton("BTN_MIN",pDC,CPoint(m_rtWnd.Width()-80,8),1);
- m_rtButtMin=m_bmRt;
- m_rtButtMinM=m_rtButtMin;//记录最小button屏幕位置
- m_rtButtMin.OffsetRect(m_rtWnd.TopLeft()); //记录最小button屏幕位置
- //NOM/MAX
- if(IsZoomed())
- FillButton("BTN_NOM",pDC,CPoint(m_bmRt.right,8),1);
- else
- FillButton("BTN_MAX",pDC,CPoint(m_bmRt.right,8),1);
- m_rtButtMax=m_bmRt;
- m_rtButtMaxM=m_rtButtMax;//记录button屏幕位置
- m_rtButtMax.OffsetRect(m_rtWnd.TopLeft());//记录button屏幕位置
- //CLS
- FillButton("BTN_CLS",pDC,CPoint(m_bmRt.right,8),1);
- m_rtButtExit=m_bmRt;
- m_rtButtExitM=m_rtButtExit;//记录关闭button屏幕位置
- m_rtButtExit.OffsetRect(m_rtWnd.TopLeft());//记录关闭button屏幕位置
- //重画caption
- int nOldtMode=pDC->SetBkMode(TRANSPARENT);
- COLORREF clOldText = pDC->SetTextColor(RGB(150, 150, 150));
- pDC->SelectStockObject(DEVICE_DEFAULT_FONT);
- CSize sz=pDC->GetTextExtent(m_strCaption);
- rtTitle.right-=GetSystemMetrics(SM_CYSMICON);
- rtTitle.top=6;
- //Caption阴影.
- pDC->DrawText(_T(m_strCaption), -1, &CRect(rtTitle.left+1,rtTitle.top+1,rtTitle.right+1,rtTitle.bottom+1), DT_CENTER);
- if(m_bNCActive)
- clOldText=pDC->SetTextColor(RGB(255, 255, 255));
- else
- clOldText=pDC->SetTextColor(RGB(100, 100, 100));
- pDC->DrawText(_T(m_strCaption), -1, &rtTitle, DT_CENTER);
- pDC->SetBkMode(nOldtMode);
- pDC->SetTextColor(clOldText);
- //重画左边框
- CRect rtborder;
- rtborder.left=0;
- rtborder.top=rtTitle.bottom;
- rtborder.right=rtborder.left+4;
- rtborder.bottom=rtborder.top+m_rtWnd.Height();
- if (m_bNCActive)
- {
- FillRtWithBmp("L",pDC,rtborder);
- }
- else
- {
- FillRtWithBmp("L_N",pDC,rtborder);
- }
- //重画右边框
- rtborder.left=m_rtWnd.Width()-4;
- //rtborder.top同左边框.
- rtborder.right=rtborder.left+4;
- rtborder.bottom=rtborder.top+m_rtWnd.Height();
- if (m_bNCActive)
- {
- FillRtWithBmp("R",pDC,rtborder);
- }
- else
- {
- FillRtWithBmp("R_N",pDC,rtborder);
- }
- //重画下边框
- rtborder.left=0;
- rtborder.top=m_rtWnd.Height()-4;
- rtborder.right=rtborder.Width();
- rtborder.bottom=rtborder.top+4;
- if(m_bNCActive)
- {
- FillRtWithBmp("BC1",pDC,rtborder);
- FillWithBmpRtUR("BC2",pDC,CPoint(rtborder.right-4,rtborder.top));
- FillWithBmpRtUL("BL",pDC,CPoint(rtborder.left,rtborder.top));
- FillWithBmpRtUL("BR",pDC,CPoint(rtborder.right-4,rtborder.top));
- }
- else
- {
- FillRtWithBmp("BC1_N",pDC,rtborder);
- FillWithBmpRtUL("BL_N",pDC,CPoint(rtborder.left,rtborder.top));
- FillWithBmpRtUL("BR_N",pDC,CPoint(rtborder.right-4,rtborder.top));
- }
- }
- }
- Draw NC end
- int CMYSkinDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
- {
- this->ModifyStyle(WS_SYSMENU, 0);//防止在任务栏右单击时出现最大最小关闭.
- GetWindowText(m_strCaption);//为自绘的标题做准备.
- if (CDialog::OnCreate(lpCreateStruct) == -1)
- return -1;
- return 0;
- }
- void CMYSkinDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
- {
- //CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
- if (bSysMenu) OnNcPaint();//防止在任务栏右键图标时窗口出现最大最小关闭
- }
- void CMYSkinDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
- {
- //检测最小,最大和关闭按钮是否有鼠标悬停,然后更换.
- if (m_rtButtExit.PtInRect(point))
- {
- CDC* pWinDC=GetWindowDC();//绘关闭按钮悬停时的图标
- FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),2);
- FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),1);
- if (IsZoomed())
- FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
- else
- FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
- ReleaseDC(pWinDC);
- }
- else if (m_rtButtMin.PtInRect(point))
- {
- //绘最小化按钮悬停时的图标
- CDC* pWinDC=GetWindowDC();
- FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),1);
- FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),2);
- if (IsZoomed())
- FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
- else
- FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
- ReleaseDC(pWinDC);
- }
- else if (m_rtButtMax.PtInRect(point))
- { //绘最大化按钮悬停时的图标
- CDC* pWinDC=GetWindowDC();
- FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),1);
- FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),1);
- if (IsZoomed())
- FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),2);
- else
- FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),2);
- ReleaseDC(pWinDC);
- }
- else
- {
- CDC* pWinDC=GetWindowDC();
- FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),1);
- FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),1);
- if (IsZoomed())
- FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
- else
- FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
- ReleaseDC(pWinDC);
- }
- }
- LRESULT CMYSkinDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
- {
- if (message == WM_NCHITTEST)
- {
- LRESULT lRet = CDialog::WindowProc(message, wParam, lParam);
- if (lRet==HTZOOM || lRet == HTMINBUTTON || lRet == HTCLOSE)//屏蔽最大最小关闭消息.
- //.net03:ms-help:wm_nchittest
- return HTCAPTION;
- else
- return lRet;
- }
- // 在OnNcActivate 和OnInitMenuPopup 中也处理最大和最小关闭误显示的问题.
- else if (message == WM_SETCURSOR ||
- message == WM_NCLBUTTONDOWN ||
- message == WM_NCLBUTTONUP ||
- message == WM_NCLBUTTONDBLCLK||
- message == WM_NCRBUTTONDOWN ||
- message == WM_NCRBUTTONDBLCLK ||
- message == 0x0125 /*WM_UNINITMENUPOPUP*/)
- {
- ModifyStyle(WS_SYSMENU, 0);//移除系统菜单.
- LRESULT lRet = CDialog::WindowProc(message, wParam, lParam);
- //ModifyStyle(0, WS_SYSMENU);
- return lRet;
- }
- return CDialog::WindowProc(message, wParam, lParam);
- }
- HBRUSH CMYSkinDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
- {
- if (nCtlColor == CTLCOLOR_DLG) return m_brBG;//对话框背景颜色.
- return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
- }
- void CMYSkinDlg::PreSubclassWindow()
- {
- // TODO: 在此添加专用代码和/或调用基类
- //得到标题栏图片高度。
- CString bmpFileName="C2";
- HBITMAP bitmap;
- bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
- "skin//"+_T(bmpFileName)+".bmp",
- IMAGE_BITMAP,
- 0,
- 0,
- LR_DEFAULTSIZE|LR_LOADFROMFILE);
- BITMAP bm;
- CBitmap cbmp;
- cbmp.Attach(bitmap);
- cbmp.GetBitmap(&bm);
- cbmp.DeleteObject();
- m_nCaptionHeight=bm.bmHeight-4;
- CDialog::PreSubclassWindow();
- }
不足之处:
(1)只针对对话框。
(2)图片重绘时的闪烁问题。
(3)图片装载时的错误处理。
(4)最大最小关闭图标依然显示的问题。
(5)border宽度不能随窗口样式改变的问题。
(6)没有解决最大最小的不使能,显/隐的问题。
(7)XP环境下的最佳性能时标题栏左上和右上角多余区域的处理。
(8)程序有待进一步简化和优化。
==================================
5.2007.9.10更新(同时祝天下所有的老师教师节快乐!)
彻底解决最大最小关闭按钮依然显示的问题:
在WindowProc中加入:
- if(message == 0x00AE|| //:WM_NCUAHDRAWCAPTION
- message == 0x00AF)//:WM_NCUAHDRAWFRAME
- {
- return WM_NCPAINT;
- }