界面编程:VC实现自绘窗体标题栏非客户区

来源:互联网 发布:网络信息监控采集技术 编辑:程序博客网 时间:2024/05/19 08:39
本程序在VC03测试成功,效果, 图片素材:从BC1.bmp到第2页的UR_N.bmp

1.准备工作:
(1)得到文件夹中的位图句柄:
首先要准备相应图片。
  1. HBITMAP bitmap;
  2. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  3.     "skin//Test.bmp",
  4.     IMAGE_BITMAP,
  5.     0,
  6.     0,
  7.     LR_DEFAULTSIZE|LR_LOADFROMFILE);

  8. CBitmap cbmp;
  9. cbmp.Attach(bitmap);
复制代码
其中,skin//Test.bmp为文件路径。

(2)关于非客户区的消息:
  1. ON_WM_NCPAINT()//绘非客户区时。
  2. ON_WM_NCACTIVATE()//非客户区有焦点和失去焦点时。
  3. ON_WM_NCCALCSIZE()//计算窗体尺寸时。
复制代码
(3)改变标题栏尺寸:
重写ON_WM_NCCALCSIZE()消息响应函数。
  1. void CMYSkinDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
  2. {
  3. //重设标题栏高度。m_nCaptionHeight在PreSubclassWindow中计算。
  4. lpncsp->rgrc[0].top+=m_nCaptionHeight-GetSystemMetrics(SM_CYCAPTION);
  5. CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
  6. }
复制代码
(4)非客户区的鼠标动作:
相关消息:
  1. ON_WM_NCLBUTTONDOWN()//鼠标下。
  2. ON_WM_NCLBUTTONUP()//鼠标上。
  3. ON_WM_NCMOUSEMOVE()//鼠标悬停。
复制代码
(5)屏蔽最大最小关闭消息:
在WindowProc中:
  1. LRESULT CMYSkinDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3. if (message == WM_NCHITTEST)
  4.     {
  5.      LRESULT lRet = CDialog::WindowProc(message, wParam, lParam);
  6.      //屏蔽最大最小关闭消息.
  7.      if (lRet==HTZOOM || lRet == HTMINBUTTON || lRet == HTCLOSE)
  8.       return HTCAPTION;//视为标题栏动作。
  9.      else
  10.       return lRet;
  11.     }
  12. }
复制代码
2.程序和注释:
(1)用户变量和函数.
  1. protected:
  2. CBrush m_brBG;//对话框背景颜色,在OnInitDialog 中初始化,在OnCtlColor中作为返回值.
  3. CString m_strCaption;//标题.
  4. CRect m_rtWnd;//整个窗体Rect.
  5. int     m_nCaptionHeight;//标题栏高度.
  6. CRect m_rtButtons;//最大,最小,关闭按钮.
  7. CRect m_rtIcon;//图标.
  8. CRect m_rtButtMin;//最小.
  9. CRect m_rtButtMax;//最大.
  10. CRect m_rtButtExit;//关闭.
  11. CRect m_rtButtMaxM;
  12. CRect m_rtButtMinM;
  13. CRect m_rtButtExitM;
  14. CRect m_bmRt;//Bitmap所在的Rect.
  15. BOOL    m_bNCActive;//窗体活动.

  16. bool FillRtWithBmp(CString bmpFileName,CDC *pDC,CRect rt);//用Bitmap添满整个rt.
  17. bool FillWithBmpRtUL(CString bmpFileName,CDC *pDC,CPoint pt);//pt为Bitmap的左上.
  18. bool FillWithBmpRtUR(CString bmpFileName,CDC *pDC, CPoint pt);//pt为Bitmap的右上.
  19. bool FillButton(CString bmpButtonName, CDC *pDC, CPoint pt,int intState);//
  20. void DrawNC(CDC* pDC);//画非客户区.
复制代码
(2)显示之前计算从图片计算标题栏高度:
  1. void CMYSkinDlg::PreSubclassWindow()
  2. {
  3. //得到标题栏图片高度。
  4. CString bmpFileName="C2";
  5. HBITMAP bitmap;
  6. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  7.     "skin//"+_T(bmpFileName)+".bmp",
  8.     IMAGE_BITMAP,
  9.     0,
  10.     0,
  11.     LR_DEFAULTSIZE|LR_LOADFROMFILE);

  12. BITMAP bm;
  13. CBitmap cbmp;
  14. cbmp.Attach(bitmap);
  15. cbmp.GetBitmap(&bm);
  16. cbmp.DeleteObject();
  17. m_nCaptionHeight=bm.bmHeight-4;

  18. CDialog::PreSubclassWindow();
  19. }
复制代码
(3)重写OnNcCalCsize:
  1. void CMYSkinDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
  2. {
  3. //重设标题栏高度。m_nCaptionHeight在PreSubclassWindow中计算。
  4. lpncsp->rgrc[0].top+=m_nCaptionHeight-GetSystemMetrics(SM_CYCAPTION);
  5. CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
  6. }
复制代码
(4)绘非客户区:
  1. void CMYSkinDlg::OnNcPaint()
  2. {
  3. CDC* pWinDC=GetWindowDC();
  4. if (pWinDC) DrawNC(pWinDC);//函数实现略。
  5. ReleaseDC(pWinDC);
  6. }
复制代码
(5)对非客户区焦点情况的处理:
  1. BOOL CMYSkinDlg::OnNcActivate(BOOL bActive)
  2. {
  3. m_bNCActive=bActive;//在DrawNC中有体现。//初始时设NC区为活动.
  4. //防止在任务栏右键图标时出现最大最小关闭
  5. OnNcPaint();//实际源程序中有细节的考虑。
  6. return true;
  7. }
复制代码
(6)响应鼠标在非客户区的事件:
鼠标在非客户区按下:
  1. void CMYSkinDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
  2. {
  3. //检测最小,最大和关闭按钮是否按下,然后更换图片.

  4. if(!IsZoomed())//防止在最大化后能拖动.
  5.     CDialog::OnNcLButtonDown(nHitTest,point);
  6. }
  7. //OnNcLButtonUp中触发最大最小关闭:
  8. void CMYSkinDlg::OnNcLButtonUp(UINT nHitTest, CPoint point)
  9. {
  10. if (m_rtButtExit.PtInRect(point))
  11.     SendMessage(WM_CLOSE);
  12. else if (m_rtButtMin.PtInRect(point))
  13.     SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM(point.x, point.y) );
  14. else if (m_rtButtMax.PtInRect(point))
  15. {
  16.     if (IsZoomed())
  17.      SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));
  18.     else
  19.      SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM(point.x, point.y) );
  20. }
  21. }
  22. //鼠标在非客户区悬停:
  23. void CMYSkinDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
  24. {
  25. //检测最小,最大和关闭按钮是否有鼠标悬停,然后更换相应图片.

  26. }
复制代码
(7)屏蔽单击程序非客户系统原有图标矩形时出现的系统菜单或动作:
  1. UINT CMYSkinDlg::OnNcHitTest(CPoint point)
  2. {
  3. CRect tst(2,2,m_nCaptionHeight+4,m_nCaptionHeight+4);
  4. tst.OffsetRect(m_rtWnd.TopLeft());//原图标屏幕位置
  5. if(tst.PtInRect(point))//最大最小关闭按钮位置.
  6.     return HTCAPTION;
  7. else if(m_rtButtMin.PtInRect(point)||
  8.      m_rtButtMax.PtInRect(point)||
  9.      m_rtButtExit.PtInRect(point))
  10.     return HTSYSMENU;//使此区域能够响应OnNcLButtonUp
  11. else
  12.     return CDialog::OnNcHitTest(point);
  13. }
复制代码
(8)系统菜单的显示和隐藏:
为了使重绘工作顺利进行而不影响程序外在表现,要对系统菜单显示和隐藏, 如在OnNcActivate中有这样的程序片段:
  1. if(bActive)
  2. {
  3.     ModifyStyle(0, WS_SYSMENU);
  4.     OnNcPaint();
  5. }
  6. else
  7. {
  8.     ModifyStyle(WS_SYSMENU, 0);
  9.     OnNcPaint();
  10. }
复制代码
又如,在OnCreate中:
  1. this->ModifyStyle(WS_SYSMENU, 0);//防止在任务栏右单击时出现最大最小关闭.
复制代码
(9)对系统最大最小关闭图标依然出现的处理:
虽然用户取消了NC区系统的重绘,但是系统仍然对最大最小关闭图标重绘(主要表现在用户右单击任务栏图标时),这里的处理方法如下:
  1. void CMYSkinDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
  2. {
  3. //CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
  4. if (bSysMenu) OnNcPaint();//防止在任务栏右键图标时窗口出现最大最小关闭
  5. }
复制代码
3.源程序:
(1)MYSkinDlg.h:
  1. ////实现
  2. protected://用户变量和函数.
  3. CBrush m_brBG;//对话框背景颜色,在OnInitDialog 中初始化,在OnCtlColor中作为返回值.
  4. CString m_strCaption;//标题.
  5. CRect m_rtWnd;//整个窗体Rect.
  6. int     m_nCaptionHeight;//标题栏高度.
  7. CRect m_rtButtons;//最大,最小,关闭按钮.
  8. CRect m_rtIcon;//图标.
  9. CRect m_rtButtMin;//最小.
  10. CRect m_rtButtMax;//最大.
  11. CRect m_rtButtExit;//关闭.
  12. CRect m_rtButtMaxM;
  13. CRect m_rtButtMinM;
  14. CRect m_rtButtExitM;
  15. CRect m_bmRt;//Bitmap所在的Rect.
  16. BOOL    m_bNCActive;//窗体活动.

  17. bool FillRtWithBmp(CString bmpFileName,CDC *pDC,CRect rt);//用Bitmap添满整个rt.
  18. bool FillWithBmpRtUL(CString bmpFileName,CDC *pDC,CPoint pt);//pt为Bitmap的左上.
  19. bool FillWithBmpRtUR(CString bmpFileName,CDC *pDC, CPoint pt);//pt为Bitmap的右上.
  20. bool FillButton(CString bmpButtonName, CDC *pDC, CPoint pt,int intState);//
  21. void DrawNC(CDC* pDC);//画非客户区.
复制代码
(2)MYSkinDlg.cpp:
  1. // MYSkinDlg.cpp : 实现文件

  2. #include "stdafx.h"
  3. #include "MYSkin.h"
  4. #include "MYSkinDlg.h"
  5. #include ".\myskindlg.h"

  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #endif

  9. // CMYSkinDlg 对话框

  10. CMYSkinDlg::CMYSkinDlg(CWnd* pParent /*=NULL*/)
  11. : CDialog(CMYSkinDlg::IDD, pParent)
  12. {
  13. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  14. }

  15. void CMYSkinDlg::DoDataExchange(CDataExchange* pDX)
  16. {
  17. CDialog::DoDataExchange(pDX);
  18. }

  19. BEGIN_MESSAGE_MAP(CMYSkinDlg, CDialog)
  20. ON_WM_PAINT()
  21. ON_WM_QUERYDRAGICON()
  22. //}}AFX_MSG_MAP
  23. ON_BN_CLICKED(IDC_BUTTON, OnBnClickedButton)
  24. ON_WM_NCPAINT()
  25. ON_WM_NCACTIVATE()
  26. ON_WM_NCCALCSIZE()
  27. ON_WM_NCHITTEST()
  28. ON_WM_NCLBUTTONDOWN()
  29. ON_WM_NCLBUTTONUP()
  30. ON_WM_MOVE()
  31. ON_WM_CREATE()
  32. ON_WM_INITMENUPOPUP()
  33. ON_WM_NCMOUSEMOVE()
  34. ON_WM_CTLCOLOR()
  35. END_MESSAGE_MAP()
  36. // CMYSkinDlg 消息处理程序

  37. BOOL CMYSkinDlg::OnInitDialog()
  38. {
  39. CDialog::OnInitDialog();

  40. // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
  41. //    执行此操作
  42. SetIcon(m_hIcon, TRUE);     // 设置大图标
  43. SetIcon(m_hIcon, FALSE);    // 设置小图标

  44. // TODO: 在此添加额外的初始化代码
  45. m_bNCActive=true;//初始NC区为活动.
  46. m_brBG.CreateSolidBrush(RGB(220, 220, 220)); //对话框背景颜色,在OnCtlColor中作为返回值.

  47. return TRUE;    // 除非设置了控件的焦点,否则返回 TRUE
  48. }

  49. // 如果向对话框添加最小化按钮,则需要下面的代码
  50. //    来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
  51. //    这将由框架自动完成。

  52. void CMYSkinDlg::OnPaint()
  53. {
  54. if (IsIconic())
  55. {
  56.     CPaintDC dc(this); // 用于绘制的设备上下文
  57.     SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  58.     // 使图标在工作矩形中居中
  59.     int cxIcon = GetSystemMetrics(SM_CXICON);
  60.     int cyIcon = GetSystemMetrics(SM_CYICON);
  61.     CRect rect;
  62.     GetClientRect(&rect);
  63.     int x = (rect.Width() - cxIcon + 1) / 2;
  64.     int y = (rect.Height() - cyIcon + 1) / 2;
  65.     // 绘制图标
  66.     dc.DrawIcon(x, y, m_hIcon);
  67. }
  68. else
  69. {
  70.     CDialog::OnPaint();
  71. }
  72. }

  73. //当用户拖动最小化窗口时系统调用此函数取得光标显示。
  74. HCURSOR CMYSkinDlg::OnQueryDragIcon()
  75. {
  76. return static_cast<HCURSOR>(m_hIcon);
  77. }

  78. void CMYSkinDlg::OnBnClickedButton()
  79. {
  80. MessageBox("Main Dlg Lost Focus!!");
  81. }
  82. /////////////////////////////////////////////////////////////////////////////////////
  83. void CMYSkinDlg::OnNcPaint()
  84. {
  85. CDC* pWinDC=GetWindowDC();
  86. if (pWinDC) DrawNC(pWinDC);
  87. ReleaseDC(pWinDC);
  88. }

  89. BOOL CMYSkinDlg::OnNcActivate(BOOL bActive)
  90. {
  91. m_bNCActive=bActive;
  92. //防止在任务栏右键图标时出现最大最小关闭
  93. if(bActive)
  94. {
  95.     ModifyStyle(0, WS_SYSMENU);
  96.     OnNcPaint();
  97. }
  98. else
  99. {
  100.     ModifyStyle(WS_SYSMENU, 0);
  101.     OnNcPaint();
  102. }
  103. return true;
  104. }

  105. void CMYSkinDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
  106. {
  107. //重设标题栏高度。m_nCaptionHeight在PreSubclassWindow中计算。
  108. lpncsp->rgrc[0].top+=m_nCaptionHeight-GetSystemMetrics(SM_CYCAPTION);
  109. CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
  110. }

  111. UINT CMYSkinDlg::OnNcHitTest(CPoint point)
  112. {
  113. CRect tst(2,2,m_nCaptionHeight+4,m_nCaptionHeight+4);
  114. tst.OffsetRect(m_rtWnd.TopLeft());//原图标屏幕位置
  115. if(tst.PtInRect(point))//最大最小关闭按钮位置.
  116.     return HTCAPTION;
  117. else if(m_rtButtMin.PtInRect(point)||
  118.      m_rtButtMax.PtInRect(point)||
  119.      m_rtButtExit.PtInRect(point))
  120.     return HTSYSMENU;//使此区域能够响应OnNcLButtonUp
  121. else
  122.     return CDialog::OnNcHitTest(point);
  123. }

  124. void CMYSkinDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
  125. {
  126. //检测最小,最大和关闭按钮是否按下,然后更换.
  127. if (m_rtButtExit.PtInRect(point))
  128. { //绘关闭按钮按下时的图标
  129.     CDC* pWinDC=GetWindowDC();
  130.     FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),3);
  131.     ReleaseDC(pWinDC);
  132. }
  133. else if (m_rtButtMin.PtInRect(point))
  134. {
  135.     //绘最小化按钮按下时的图标
  136.     CDC* pWinDC=GetWindowDC();
  137.     FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),3);
  138.     ReleaseDC(pWinDC);
  139. }
  140. else if (m_rtButtMax.PtInRect(point))
  141. { //绘最大化按钮按下时的图标
  142.     CDC* pWinDC=GetWindowDC();
  143.     if (IsZoomed())
  144.      FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),3);
  145.     else
  146.      FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),3);
  147.     ReleaseDC(pWinDC);
  148. }
  149. if(!IsZoomed())//防止在最大化后能拖动.
  150.     CDialog::OnNcLButtonDown(nHitTest,point);
  151. }

  152. void CMYSkinDlg::OnNcLButtonUp(UINT nHitTest, CPoint point)
  153. {
  154. if (m_rtButtExit.PtInRect(point))
  155.     SendMessage(WM_CLOSE);
  156. else if (m_rtButtMin.PtInRect(point))
  157.     SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM(point.x, point.y) );
  158. else if (m_rtButtMax.PtInRect(point))
  159. {
  160.     if (IsZoomed())
  161.      SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));
  162.     else
  163.      SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM(point.x, point.y) );
  164. }
  165. }

  166. void CMYSkinDlg::OnMove(int x, int y)
  167. {
  168. CDialog::OnMove(x, y);
  169. //OnNcPaint();//减少闪烁,不用OnNcPaint()。
  170. //整个Window的相对于屏幕的矩形
  171. GetWindowRect(&m_rtWnd); //更新窗体矩形.

  172. m_rtButtMinM.OffsetRect(m_rtWnd.TopLeft()); //记录最小button屏幕位置
  173. m_rtButtMin=m_rtButtMinM;//记录最小button屏幕位置
  174. m_rtButtMinM.OffsetRect(-m_rtWnd.TopLeft());//还原.

  175. m_rtButtMaxM.OffsetRect(m_rtWnd.TopLeft());//记录button屏幕位置
  176. m_rtButtMax=m_rtButtMaxM;//记录button屏幕位置
  177. m_rtButtMaxM.OffsetRect(-m_rtWnd.TopLeft());//还原.

  178. m_rtButtExitM.OffsetRect(m_rtWnd.TopLeft());//记录关闭button屏幕位置
  179. m_rtButtExit=m_rtButtExitM;//记录关闭button屏幕位置
  180. m_rtButtExitM.OffsetRect(-m_rtWnd.TopLeft());//还原.
  181. }

  182. ////////////Bitmap Load Function//////////////////////////////////
  183. bool CMYSkinDlg::FillRtWithBmp(CString bmpFileName,CDC *pDC,CRect rt)
  184. {
  185. //填满整个矩形
  186. HBITMAP bitmap;
  187. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  188.           "skin//"+_T(bmpFileName)+".bmp",
  189.           IMAGE_BITMAP,
  190.           0,
  191.           0,
  192.           LR_DEFAULTSIZE|LR_LOADFROMFILE);
  193. BITMAP bm;  
  194. CBitmap cbmp;
  195. CDC MemDC;

  196. cbmp.Attach(bitmap);
  197. cbmp.GetBitmap(&bm);
  198. MemDC.CreateCompatibleDC (pDC);
  199. CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);

  200. CRect rttmp;
  201. rttmp=rt;
  202. int i=0;
  203. int j=0;

  204. if(rt.Width()/bm.bmWidth<1 && rt.Height()/bm.bmHeight<1)
  205.     pDC->BitBlt(rt.left,rt.top,rt.Width(),rt.Height(),&MemDC,0,0,SRCCOPY);
  206. else if(rt.Width()/bm.bmWidth<1 && rt.Height()/bm.bmHeight>1)
  207.     for (j=0; j<=rt.Height()/bm.bmHeight;j++)
  208.      {
  209.       pDC->BitBlt(rt.left,rttmp.top,rt.Width(),bm.bmHeight,&MemDC,0,0,SRCCOPY);
  210.       rttmp.top+=bm.bmHeight;
  211.      }
  212. else if(rt.Width()/bm.bmWidth>1 && rt.Height()/bm.bmHeight<1)
  213.     for (i=0;i<=rt.Width()/bm.bmWidth;i++)
  214.      {
  215.       pDC->BitBlt(rttmp.left,rt.top,bm.bmWidth,rt.Height(),&MemDC,0,0,SRCCOPY);
  216.       rttmp.left+=bm.bmWidth;
  217.      }
  218. else
  219. {
  220.     for (i=0;i<rt.Width()/bm.bmWidth;i++)
  221.     {
  222.      for (j=0; j<rt.Height()/bm.bmHeight;j++)
  223.      {
  224.       pDC->BitBlt(rttmp.left,rttmp.top,bm.bmWidth,bm.bmHeight,&MemDC,0,0,SRCCOPY);
  225.       rttmp.top+=bm.bmHeight;
  226.      }
  227.      rttmp.top=rt.top;
  228.      rttmp.left+=bm.bmWidth;
  229.     }
  230.     rttmp=rt;
  231.     for (i=0;i<rt.Width()/bm.bmWidth;i++)
  232.     {
  233.      pDC->BitBlt(rttmp.left,
  234.         rt.top+rt.Height()-(rt.Height()%bm.bmHeight),
  235.            bm.bmWidth,
  236.         rt.Height()%bm.bmHeight,
  237.         &MemDC,0,0,SRCCOPY);
  238.      rttmp.left+=bm.bmWidth;
  239.     }
  240.     rttmp=rt;
  241.     for (j=0;j<rt.Height()/bm.bmHeight;j++)
  242.     {
  243.      pDC->BitBlt(rt.left+rt.Width()-(rt.Width()%bm.bmWidth),
  244.         rttmp.top,
  245.         rt.Width()%bm.bmWidth,
  246.         bm.bmHeight,
  247.         &MemDC,0,0,SRCCOPY);
  248.      rttmp.top+=bm.bmHeight;
  249.     }
  250.     rttmp=rt;
  251.     pDC->BitBlt(rt.left+rt.Width()-(rt.Width()%bm.bmWidth),
  252.        rt.top+rt.Height()-(rt.Height()%bm.bmHeight),
  253.        rt.Width()%bm.bmWidth,
  254.        rt.Height()%bm.bmHeight,
  255.        &MemDC,0,0,SRCCOPY);

  256. }
  257. MemDC.SelectObject (pOldBitmap);
  258. ReleaseDC(&MemDC);
  259. cbmp.DeleteObject();
  260. pOldBitmap->DeleteObject();
  261. return true;
  262. }

  263. bool CMYSkinDlg::FillWithBmpRtUL(CString bmpFileName,CDC *pDC, CPoint pt)
  264. {
  265. //只填一张Bitmap.参考点pt为图片左上.
  266. HBITMAP bitmap;
  267. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  268.           "skin//"+_T(bmpFileName)+".bmp",
  269.           IMAGE_BITMAP,
  270.           0,
  271.           0,
  272.           LR_DEFAULTSIZE|LR_LOADFROMFILE);

  273. BITMAP bm;  
  274. CBitmap cbmp;

  275. cbmp.Attach(bitmap);
  276. cbmp.GetBitmap(&bm);

  277. m_bmRt.left=pt.x;//m_bmRt为全局.
  278. m_bmRt.top=pt.y;
  279. m_bmRt.right=pt.x + bm.bmWidth;
  280. m_bmRt.bottom=pt.y + bm.bmHeight;

  281. CDC MemDC;
  282. MemDC.CreateCompatibleDC (pDC);
  283. CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);

  284. pDC->BitBlt(pt.x,pt.y,pt.x+bm.bmWidth ,pt.y+bm.bmHeight ,&MemDC,0,0,SRCCOPY);
  285. MemDC.SelectObject (pOldBitmap);
  286. ReleaseDC(&MemDC);
  287. cbmp.DeleteObject();
  288. pOldBitmap->DeleteObject();
  289. return true;
  290. }

  291. bool CMYSkinDlg::FillWithBmpRtUR(CString bmpFileName,CDC *pDC, CPoint pt)
  292. {
  293. //只填一张图,参考点pt为右上.
  294. HBITMAP bitmap;
  295. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  296.           "skin//"+_T(bmpFileName)+".bmp",
  297.           IMAGE_BITMAP,
  298.           0,
  299.           0,
  300.           LR_DEFAULTSIZE|LR_LOADFROMFILE);

  301. BITMAP bm;  
  302. CBitmap cbmp;

  303. cbmp.Attach(bitmap);
  304. cbmp.GetBitmap(&bm);

  305. m_bmRt.left=pt.x - bm.bmWidth;//m_bmRt为全局.
  306. m_bmRt.top=pt.y;
  307. m_bmRt.right=pt.x;
  308. m_bmRt.bottom=pt.y + bm.bmHeight;

  309. CDC MemDC;
  310. MemDC.CreateCompatibleDC (pDC);
  311. CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);

  312. pDC->BitBlt(pt.x-bm.bmWidth,pt.y,pt.x,pt.y+bm.bmHeight ,&MemDC,0,0,SRCCOPY);
  313. MemDC.SelectObject (pOldBitmap);
  314. ReleaseDC(&MemDC);
  315. cbmp.DeleteObject();
  316. pOldBitmap->DeleteObject();
  317. return true;
  318. }
  319. bool CMYSkinDlg::FillButton(CString bmpButtonName, CDC *pDC, CPoint pt,int intState)
  320. {
  321. HBITMAP bitmap;
  322. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  323.     "skin//"+_T(bmpButtonName)+".bmp",
  324.     IMAGE_BITMAP,
  325.     0,
  326.     0,
  327.     LR_DEFAULTSIZE|LR_LOADFROMFILE);

  328. BITMAP bm;  
  329. CBitmap cbmp;

  330. cbmp.Attach(bitmap);
  331. cbmp.GetBitmap(&bm);

  332. CDC MemDC;
  333. MemDC.CreateCompatibleDC (pDC);
  334. CBitmap *pOldBitmap=MemDC.SelectObject (&cbmp);

  335. switch(intState)
  336. {
  337. case 1:
  338.     pDC->BitBlt(pt.x, pt.y, bm.bmWidth / 3, bm.bmHeight,&MemDC,
  339.      0,0,SRCCOPY);
  340.     break;
  341. case 2:
  342.     pDC->BitBlt(pt.x, pt.y, bm.bmWidth / 3, bm.bmHeight,&MemDC,
  343.      bm.bmWidth / 3, 0, SRCCOPY);
  344.     break;
  345. case 3:
  346.     pDC->BitBlt(pt.x, pt.y, bm.bmWidth / 3, bm.bmHeight,&MemDC,
  347.      bm.bmWidth * 2 / 3, 0, SRCCOPY);
  348.     break;
  349. }
  350. m_bmRt.left = pt.x;//m_bmRt为全局.
  351. m_bmRt.top = pt.y;
  352. m_bmRt.right = pt.x + bm.bmWidth / 3;
  353. m_bmRt.bottom = pt.y + bm.bmHeight;


  354. MemDC.SelectObject (pOldBitmap);
  355. ReleaseDC(&MemDC);
  356. cbmp.DeleteObject();
  357. pOldBitmap->DeleteObject();

  358. return true;
  359. }
  360. ////////////Bitmap Load Function end///////////////////////

  361. ////////////Draw NC////////////////////////////////////////
  362. void CMYSkinDlg::DrawNC(CDC* pDC)
  363. {
  364. if (m_hWnd)
  365. {
  366.     CRect rtTitle;//

  367.     //整个Window的相对于屏幕的矩形
  368.     GetWindowRect(&m_rtWnd);
  369.     //取得整个Title bar的矩形
  370.     rtTitle.left=0;
  371.     rtTitle.top=0;
  372.     rtTitle.right=m_rtWnd.Width()+3;
  373.     rtTitle.bottom=rtTitle.top+m_nCaptionHeight + 4;
  374.     //重画Title Bar
  375.     if (m_bNCActive)
  376.     {
  377.      FillWithBmpRtUL("UL",pDC,CPoint(0,0));
  378.      CRect rtUL=m_bmRt;
  379.      FillWithBmpRtUR("C2",pDC,CPoint(rtTitle.right-3,rtTitle.top));
  380.      FillRtWithBmp("C1",pDC,CRect(rtTitle.left+rtUL.Width(),rtTitle.top,rtTitle.right-m_bmRt.Width()-3,rtTitle.bottom));
  381.     }
  382.     else
  383.     {
  384.      FillWithBmpRtUL("UL_N",pDC,CPoint(0,0));
  385.      CRect rtUL=m_bmRt;
  386.      FillWithBmpRtUR("C2_N",pDC,CPoint(rtTitle.right-3,rtTitle.top));
  387.      FillRtWithBmp("C1_N",pDC,CRect(rtTitle.left+rtUL.Width(),rtTitle.top,rtTitle.right-m_bmRt.Width()-3,rtTitle.bottom));
  388.     }
  389.     //重画icon
  390.     HICON hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  391.     m_rtIcon.left=rtTitle.left+15;
  392.     m_rtIcon.top=rtTitle.top+2;
  393.     m_rtIcon.right=m_rtIcon.left+rtTitle.Height()-5;
  394.     m_rtIcon.bottom=m_rtIcon.top+rtTitle.Height()-5;

  395.     ::DrawIconEx(pDC->m_hDC,
  396.        m_rtIcon.left, m_rtIcon.top,
  397.        hIcon,
  398.        m_rtIcon.Height(), m_rtIcon.Width(),
  399.        0, NULL,DI_NORMAL);
  400.     m_rtIcon.OffsetRect(m_rtWnd.TopLeft()); //记录Icon屏幕位置

  401.     //重画最大最小关闭button
  402.     //MIN
  403.     FillButton("BTN_MIN",pDC,CPoint(m_rtWnd.Width()-80,8),1);
  404.     m_rtButtMin=m_bmRt;
  405.     m_rtButtMinM=m_rtButtMin;//记录最小button屏幕位置
  406.     m_rtButtMin.OffsetRect(m_rtWnd.TopLeft()); //记录最小button屏幕位置

  407.     //NOM/MAX
  408.     if(IsZoomed())
  409.      FillButton("BTN_NOM",pDC,CPoint(m_bmRt.right,8),1);
  410.     else
  411.      FillButton("BTN_MAX",pDC,CPoint(m_bmRt.right,8),1);
  412.     m_rtButtMax=m_bmRt;
  413.     m_rtButtMaxM=m_rtButtMax;//记录button屏幕位置
  414.     m_rtButtMax.OffsetRect(m_rtWnd.TopLeft());//记录button屏幕位置

  415.     //CLS
  416.     FillButton("BTN_CLS",pDC,CPoint(m_bmRt.right,8),1);
  417.     m_rtButtExit=m_bmRt;
  418.     m_rtButtExitM=m_rtButtExit;//记录关闭button屏幕位置
  419.     m_rtButtExit.OffsetRect(m_rtWnd.TopLeft());//记录关闭button屏幕位置

  420.     //重画caption
  421.     int nOldtMode=pDC->SetBkMode(TRANSPARENT);
  422.     COLORREF clOldText = pDC->SetTextColor(RGB(150, 150, 150));
  423.     pDC->SelectStockObject(DEVICE_DEFAULT_FONT);
  424.     CSize sz=pDC->GetTextExtent(m_strCaption);
  425.     rtTitle.right-=GetSystemMetrics(SM_CYSMICON);
  426.     rtTitle.top=6;
  427.     //Caption阴影.
  428.     pDC->DrawText(_T(m_strCaption), -1, &CRect(rtTitle.left+1,rtTitle.top+1,rtTitle.right+1,rtTitle.bottom+1), DT_CENTER);
  429.     if(m_bNCActive)
  430.      clOldText=pDC->SetTextColor(RGB(255, 255, 255));
  431.     else
  432.      clOldText=pDC->SetTextColor(RGB(100, 100, 100));
  433.     pDC->DrawText(_T(m_strCaption), -1, &rtTitle, DT_CENTER);
  434.     pDC->SetBkMode(nOldtMode);
  435.     pDC->SetTextColor(clOldText);

  436.     //重画左边框
  437.     CRect rtborder;
  438.     rtborder.left=0;
  439.     rtborder.top=rtTitle.bottom;
  440.     rtborder.right=rtborder.left+4;
  441.     rtborder.bottom=rtborder.top+m_rtWnd.Height();
  442.     if (m_bNCActive)
  443.     {
  444.      FillRtWithBmp("L",pDC,rtborder);
  445.     }
  446.     else
  447.     {
  448.      FillRtWithBmp("L_N",pDC,rtborder);
  449.     }

  450.     //重画右边框
  451.     rtborder.left=m_rtWnd.Width()-4;
  452.     //rtborder.top同左边框.
  453.     rtborder.right=rtborder.left+4;
  454.     rtborder.bottom=rtborder.top+m_rtWnd.Height();
  455.     if (m_bNCActive)
  456.     {
  457.      FillRtWithBmp("R",pDC,rtborder);
  458.     }
  459.     else
  460.     {
  461.      FillRtWithBmp("R_N",pDC,rtborder);
  462.     }
  463.     //重画下边框
  464.     rtborder.left=0;
  465.     rtborder.top=m_rtWnd.Height()-4;
  466.     rtborder.right=rtborder.Width();
  467.     rtborder.bottom=rtborder.top+4;
  468.     if(m_bNCActive)
  469.     {
  470.               FillRtWithBmp("BC1",pDC,rtborder);
  471.      FillWithBmpRtUR("BC2",pDC,CPoint(rtborder.right-4,rtborder.top));
  472.      FillWithBmpRtUL("BL",pDC,CPoint(rtborder.left,rtborder.top));
  473.      FillWithBmpRtUL("BR",pDC,CPoint(rtborder.right-4,rtborder.top));
  474.     }
  475.     else
  476.     {
  477.      FillRtWithBmp("BC1_N",pDC,rtborder);
  478.      FillWithBmpRtUL("BL_N",pDC,CPoint(rtborder.left,rtborder.top));
  479.      FillWithBmpRtUL("BR_N",pDC,CPoint(rtborder.right-4,rtborder.top));
  480.     }
  481. }
  482. }
  483. ////////////Draw NC end////////////////////////////////////////

  484. int CMYSkinDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
  485. {
  486. this->ModifyStyle(WS_SYSMENU, 0);//防止在任务栏右单击时出现最大最小关闭.
  487. GetWindowText(m_strCaption);//为自绘的标题做准备.

  488. if (CDialog::OnCreate(lpCreateStruct) == -1)
  489.     return -1;
  490. return 0;
  491. }

  492. void CMYSkinDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
  493. {
  494. //CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
  495. if (bSysMenu) OnNcPaint();//防止在任务栏右键图标时窗口出现最大最小关闭
  496. }

  497. void CMYSkinDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
  498. {
  499. //检测最小,最大和关闭按钮是否有鼠标悬停,然后更换.
  500. if (m_rtButtExit.PtInRect(point))
  501. {
  502.     CDC* pWinDC=GetWindowDC();//绘关闭按钮悬停时的图标
  503.     FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),2);
  504.     FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),1);
  505.     if (IsZoomed())
  506.      FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
  507.     else
  508.      FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
  509.     ReleaseDC(pWinDC);
  510. }
  511. else if (m_rtButtMin.PtInRect(point))
  512. {
  513.     //绘最小化按钮悬停时的图标
  514.     CDC* pWinDC=GetWindowDC();
  515.     FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),1);
  516.     FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),2);
  517.     if (IsZoomed())
  518.      FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
  519.     else
  520.      FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
  521.     ReleaseDC(pWinDC);
  522. }
  523. else if (m_rtButtMax.PtInRect(point))
  524. { //绘最大化按钮悬停时的图标
  525.     CDC* pWinDC=GetWindowDC();
  526.     FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),1);
  527.     FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),1);
  528.     if (IsZoomed())
  529.      FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),2);
  530.     else
  531.      FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),2);
  532.     ReleaseDC(pWinDC);
  533. }
  534. else
  535. {
  536.     CDC* pWinDC=GetWindowDC();
  537.     FillButton("BTN_CLS",pWinDC,CPoint(m_rtButtExitM.left,m_rtButtExitM.top),1);
  538.     FillButton("BTN_MIN",pWinDC,CPoint(m_rtButtMinM.left,m_rtButtMinM.top),1);
  539.     if (IsZoomed())
  540.      FillButton("BTN_NOM",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
  541.     else
  542.      FillButton("BTN_MAX",pWinDC,CPoint(m_rtButtMaxM.left,m_rtButtMaxM.top),1);
  543.     ReleaseDC(pWinDC);
  544. }
  545. }

  546. LRESULT CMYSkinDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  547. {
  548. if (message == WM_NCHITTEST)
  549.     {
  550.      LRESULT lRet = CDialog::WindowProc(message, wParam, lParam);
  551.      if (lRet==HTZOOM || lRet == HTMINBUTTON || lRet == HTCLOSE)//屏蔽最大最小关闭消息.
  552.       //.net03:ms-help:wm_nchittest
  553.       return HTCAPTION;
  554.      else
  555.       return lRet;
  556.     }
  557. // 在OnNcActivate 和OnInitMenuPopup 中也处理最大和最小关闭误显示的问题.
  558. else if (message == WM_SETCURSOR ||
  559.      message == WM_NCLBUTTONDOWN ||
  560.      message == WM_NCLBUTTONUP ||
  561.      message == WM_NCLBUTTONDBLCLK||
  562.      message == WM_NCRBUTTONDOWN ||
  563.      message == WM_NCRBUTTONDBLCLK ||
  564.      message == 0x0125 /*WM_UNINITMENUPOPUP*/)
  565.     {
  566.      ModifyStyle(WS_SYSMENU, 0);//移除系统菜单.
  567.      LRESULT lRet = CDialog::WindowProc(message, wParam, lParam);
  568.      //ModifyStyle(0, WS_SYSMENU);
  569.      return lRet;
  570.     }
  571. return CDialog::WindowProc(message, wParam, lParam);
  572. }

  573. HBRUSH CMYSkinDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  574. {
  575. if (nCtlColor == CTLCOLOR_DLG) return m_brBG;//对话框背景颜色.
  576. return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
  577. }

  578. void CMYSkinDlg::PreSubclassWindow()
  579. {
  580. // TODO: 在此添加专用代码和/或调用基类
  581. //得到标题栏图片高度。
  582. CString bmpFileName="C2";
  583. HBITMAP bitmap;
  584. bitmap=(HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  585.     "skin//"+_T(bmpFileName)+".bmp",
  586.     IMAGE_BITMAP,
  587.     0,
  588.     0,
  589.     LR_DEFAULTSIZE|LR_LOADFROMFILE);

  590. BITMAP bm;
  591. CBitmap cbmp;
  592. cbmp.Attach(bitmap);
  593. cbmp.GetBitmap(&bm);
  594. cbmp.DeleteObject();
  595. m_nCaptionHeight=bm.bmHeight-4;

  596. CDialog::PreSubclassWindow();
  597. }
复制代码
4.改进
不足之处:
(1)只针对对话框。
(2)图片重绘时的闪烁问题。
(3)图片装载时的错误处理。
(4)最大最小关闭图标依然显示的问题。
(5)border宽度不能随窗口样式改变的问题。
(6)没有解决最大最小的不使能,显/隐的问题。
(7)XP环境下的最佳性能时标题栏左上和右上角多余区域的处理。
(8)程序有待进一步简化和优化。

==================================
5.2007.9.10更新(同时祝天下所有的老师教师节快乐!)

彻底解决最大最小关闭按钮依然显示的问题:
在WindowProc中加入:
  1. if(message == 0x00AE|| //:WM_NCUAHDRAWCAPTION
  2.   message == 0x00AF)//:WM_NCUAHDRAWFRAME
  3. {
  4.       return WM_NCPAINT;
  5. }
复制代码
去掉所有ModifyStyle(0, WS_SYSMENU);和ModifyStyle(WS_SYSMENU, 0);的程序片段和相关语句.
原创粉丝点击