VC++界面美化---模仿MS Office 选项对话框

来源:互联网 发布:手机坐标放线软件 编辑:程序博客网 时间:2024/05/22 16:39

---把时髦的技术挂在嘴边,还不如把过时的技术记在心里。

先来看看两幅图:

下图一是早期的Windows系统中常见的用于选项设置的窗体

 


下图二是MS Office Word的选项对话框



默认情况下用MFC的CPropertySheet和CPropertyPage创建的属性页就如图一的样子,要实现图二的样子需要做一些额外的工作:

技术及工具集:VS2010,MFC,GDI+

一、首先设计自绘按钮类CCustomButton

#pragma once/**author:hels *Date:2013-10-01*Ddescription:自绘按钮类,继承至CBitmapButton。在创建一个位图按钮控件时,设置BS_OWNERDRAW,这样,Windows就会为该按钮发送WM_MEASUREITEM和WM_DRAWITEM消息,由框架处理这些消息并维护按钮的外观。*/// CCustomButtonclass CCustomButton : public CBitmapButton{public:CCustomButton();virtual ~CCustomButton();public:void Init(UINT uNormal, UINT uHover, UINT uDown, UINT uDisable, LPCTSTR lpType, HMODULE hModule = NULL);void Check(BOOL bCheck);void Enable(BOOL bEnable);//设置按钮所在对话框的背景图片及位置,方便绘制按钮没有Focus和Selected时的状态void SetDlgBG(Bitmap* DlgBg , CRect btnArea);void SetText(CString strText){ m_strText = strText; }void SetFontName(CString strName){ m_strFontName = strName; } void SetTextColor(Color crText){ m_crText = crText; } void SetBackgroundColor(Color crBackground){ m_crBackground = crBackground; } public:virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);static BOOL ImageFromIDResource(UINT nID, LPCTSTR lpType, Image * &pImg, HINSTANCE hModule = NULL);protected:DECLARE_MESSAGE_MAP()public:afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnLButtonUp(UINT nFlags, CPoint point);afx_msg void OnMouseMove(UINT nFlags, CPoint point);afx_msg void OnMouseLeave();afx_msg BOOL OnEraseBkgnd(CDC* pDC);protected:Image*m_pNormalImg;Image*m_pHoverImg;Image*m_pDownImg;Image*m_pDisableImg;UINTm_uNormal;UINTm_uHover;UINTm_uDown;UINTm_uDisable;UINTm_uState;BOOLm_bCheck;BOOLm_bEnable;BOOLm_bHover;Color m_crBackground;Color m_crText;CString m_strText;CString m_strFontName;CRect m_rtArea;Image *m_pDlgBGImg;};

CCustomButton关键实现:

void CCustomButton::Init( UINT uNormal, UINT uHover, UINT uDown, UINT uDisable, LPCTSTR lpType, HMODULE hModule /*= NULL*/ ){ImageFromIDResource(uNormal, lpType, m_pNormalImg, hModule);ImageFromIDResource(uHover, lpType, m_pHoverImg, hModule);ImageFromIDResource(uDown, lpType, m_pDownImg, hModule);ImageFromIDResource(uDisable, lpType, m_pDisableImg, hModule);m_uNormal = uNormal;m_uHover = uHover;m_uDown = uDown;m_uDisable = uDisable;}void CCustomButton::OnLButtonDown(UINT nFlags, CPoint point){// TODO: Add your message handler code here and/or call defaultm_uState = ODS_SELECTED;CBitmapButton::OnLButtonDown(nFlags, point);}void CCustomButton::OnLButtonUp(UINT nFlags, CPoint point){// TODO: Add your message handler code here and/or call defaultm_uState = ODS_FOCUS;CBitmapButton::OnLButtonUp(nFlags, point);}void CCustomButton::OnMouseMove(UINT nFlags, CPoint point){// TODO: Add your message handler code here and/or call defaultif (m_uState & ODS_SELECTED){return;}else{m_uState = ODS_FOCUS;}//默认情况下,窗口是不响应WM_MOUSELEAVE和WM_MOUSEHOVER消息的,//所以要使用_TrackMouseEvent函数来激活这两个消息。//调用这个函数后,当鼠标在指定窗口上停留超过一定时间或离开窗口后,//该函数会Post这两个消息到指定窗口TRACKMOUSEEVENT csTME;csTME.cbSize = sizeof(csTME);csTME.dwFlags = TME_LEAVE;csTME.hwndTrack = m_hWnd;::TrackMouseEvent(&csTME);//这里自己实现Hover的记录,不需要WM_MOUSEHOVER消息,因为//WM_MOUSEHOVER时不需要更新按钮状态和图片,完全不需要管它//只处理进入和离开时的图片切换即可if (m_bHover == FALSE){//这里使用RedrawWindow而不用InvalidateRect等,是希望马上进行刷新操作,对于这两个函数以及WM_PAINT的关系,下面两个网址有详细的解释//http://baike.baidu.com/link?url=iBXyaBZaId7DEMTtnOhUymJTKXloSl0SRV310SqEvdcGRMetqYk05cvKTV3RgYNzIhgby8YnSOTGbNM9jpOUwK//http://hi.baidu.com/aidfan/item/93e062758f50602ad7a89c38RedrawWindow();m_bHover = TRUE;}}void CCustomButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){CBitmap bitmap;CDC memDC;CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);pDC->SetBkMode(TRANSPARENT);CRect rect = lpDrawItemStruct->rcItem;bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());memDC.CreateCompatibleDC(pDC);memDC.SelectObject(&bitmap);Graphics graphics(pDC->GetSafeHdc());Bitmap *button = new Bitmap(rect.Width(), rect.Height());Graphics graphicsBtn(button);graphicsBtn.Clear(m_crBackground);RectF rectDst(0.0f, 0.0f, rect.Width(), rect.Height());if(m_pDlgBGImg){RectF rectBtnArea(m_rtArea.left, m_rtArea.top,m_rtArea.Width(), m_rtArea.Height());graphicsBtn.DrawImage(m_pDlgBGImg,rectDst,m_rtArea.left, m_rtArea.top,m_rtArea.Width(), m_rtArea.Height(),UnitPixel);}if (FALSE == m_bEnable){if (m_pDisableImg){graphicsBtn.DrawImage(m_pDisableImg, rectDst);}}else if (m_bCheck || ODS_SELECTED == m_uState){graphicsBtn.DrawImage(m_pDownImg, rectDst);}else if (ODS_FOCUS == m_uState){graphicsBtn.DrawImage(m_pHoverImg, rectDst);}else{graphicsBtn.DrawImage(m_pNormalImg, rectDst);}if (m_strText.GetLength() > 0){WCHAR* pWc = m_strText.AllocSysString();WCHAR* pFontName = m_strFontName.AllocSysString();Gdiplus::Font font(pFontName, 9);SysFreeString(pFontName);StringFormat strFormat;strFormat.SetAlignment(StringAlignmentCenter);strFormat.SetLineAlignment(StringAlignmentCenter);RectF rcBound;graphicsBtn.MeasureString(pWc,-1,&font,PointF(0,0), &rcBound);rectDst.Y += 1.0;SolidBrush br(m_crText);rectDst.Offset(0, 2);graphicsBtn.DrawString(pWc, -1, &font, rectDst, &strFormat, &br);SysFreeString(pWc);}bitmap.DeleteObject();memDC.DeleteDC();graphics.DrawImage(button, 0, 0);}void CCustomButton::OnMouseLeave(){// TODO: Add your message handler code here and/or call defaultm_uState = 0 ;m_bHover = FALSE ;RedrawWindow() ;}

二、设计Button和每个选项对话框的管理类CCustomPropertyPage,简单设计一下,只是为了方便示例

class CCustomPropertyPage{public:CCustomPropertyPage(void);~CCustomPropertyPage(void);public:UINT m_uID;CRect m_rtArea;CCustomButton* m_pPageBtn;CPropertyPageEx* m_pPropertyPage;UINT m_uDlgID;};
--------------------------------------------------------
CCustomPropertyPage::CCustomPropertyPage(void){m_pPageBtn = NULL;m_pPropertyPage = NULL;}CCustomPropertyPage::~CCustomPropertyPage(void){if(m_pPageBtn){m_pPageBtn->DestroyWindow();delete m_pPageBtn;}if(m_pPropertyPage){m_pPropertyPage->DestroyWindow();delete m_pPropertyPage;}}

三、设计并实现选项对话框COptionsDialog类

以下列出关键部分代码:
CRect rect;GetClientRect(&rect);m_pDlgBGImage = new Bitmap(rect.Width(), rect.Height());Graphics graphicsBg(m_pDlgBGImage);graphicsBg.Clear(Color::White);//贴一个渐变的背景并绘制分割框Pen pen(Color(128, 128, 128));Image* bgImage;CCustomButton::ImageFromIDResource(IDB__BACKGROUND, _T("PNG"), bgImage, theApp.m_hInstance);TextureBrush bgTextureBrush(bgImage);rect = m_rtButtons;graphicsBg.FillRectangle(&bgTextureBrush, rect.left, rect.top, rect.Width(), rect.Height());rect.InflateRect(1, 1, 1, 1);graphicsBg.DrawRectangle(&pen, rect.left, rect.top, rect.Width() -1, rect.Height() -1);rect = m_rtPropPage;graphicsBg.FillRectangle(&bgTextureBrush, rect.left, rect.top, rect.Width(), rect.Height());rect.InflateRect(1,1,1,1);graphicsBg.DrawRectangle(&pen, rect.left, rect.top, rect.Width()-1, rect.Height()-1 ) ;//初始化按钮,这里准备了两个PNG按钮图片,分别用于按钮处于按下和悬停状态CCustomButton* btn0 = new CCustomButton;btn0->Init(IDB_BTN_NORMAL, IDB_BTN_HOVER, IDB_BTN_DOWN, IDB_BTN_DOWN, _T("PNG"), theApp.m_hInstance);btn0->SetText(_T("常用"));CCustomPropertyPage *page0 = new CCustomPropertyPage;page0->m_uID = 0;page0->m_pPageBtn = btn0;page0->m_uDlgID = IDD_DIALOG1;//page0->m_pPropertyPage = new CPropertyPageEx(IDD_DIALOG1);m_PropPageArray.push_back(page0);CCustomButton* btn1 = new CCustomButton;btn1->Init(IDB_BTN_NORMAL, IDB_BTN_HOVER, IDB_BTN_DOWN, IDB_BTN_DOWN, _T("PNG"), theApp.m_hInstance);btn1->SetText(_T("显示"));CCustomPropertyPage *page1 = new CCustomPropertyPage;page1->m_uID = 1;page1->m_pPageBtn = btn1;page1->m_uDlgID = IDD_DIALOG2;//page1->m_pPropertyPage = new CPropertyPageEx(IDD_DIALOG2);m_PropPageArray.push_back(page1);CCustomButton* btn2 = new CCustomButton;btn2->Init(IDB_BTN_NORMAL, IDB_BTN_HOVER, IDB_BTN_DOWN, IDB_BTN_DOWN, _T("PNG"), theApp.m_hInstance);btn2->SetText(_T("自定义"));CCustomPropertyPage *page2 = new CCustomPropertyPage;page2->m_uID = 2;page2->m_pPageBtn = btn2;page2->m_uDlgID = IDD_DIALOG1;//page2->m_pPropertyPage = new CPropertyPageEx(IDD_DIALOG1);m_PropPageArray.push_back(page2);CCustomButton* btn3 = new CCustomButton;btn3->Init(IDB_BTN_NORMAL, IDB_BTN_HOVER, IDB_BTN_DOWN, IDB_BTN_DOWN, _T("PNG"), theApp.m_hInstance);btn3->SetText(_T("加载项"));CCustomPropertyPage *page3 = new CCustomPropertyPage;page3->m_uID = 3;page3->m_pPageBtn = btn3;page3->m_uDlgID = IDD_DIALOG2;//page3->m_pPropertyPage = new CPropertyPageEx(IDD_DIALOG2);m_PropPageArray.push_back(page3);CCustomButton* btn4 = new CCustomButton;btn4->Init(IDB_BTN_NORMAL, IDB_BTN_HOVER, IDB_BTN_DOWN, IDB_BTN_DOWN, _T("PNG"), theApp.m_hInstance);btn4->SetText(_T("信任中心"));CCustomPropertyPage *page4 = new CCustomPropertyPage;page4->m_uID = 4;page4->m_pPageBtn = btn4;page4->m_uDlgID = IDD_DIALOG1;//page4->m_pPropertyPage = new CPropertyPageEx(IDD_DIALOG1);m_PropPageArray.push_back(page4);//设置按钮位置CRect rt(m_rtButtons);rt.right -= 4  ;rt.left += 4 ;rt.top += 4 ;rt.bottom = rt.top + 30 ;//按钮创建,注意指定BS_OWNERDRAW标识for(int i = 0; i < m_PropPageArray.size(); ++i){if(m_PropPageArray.at(i)->m_pPageBtn){//保存按钮区域m_PropPageArray.at(i)->m_rtArea = rt;//创建按钮m_PropPageArray.at(i)->m_pPageBtn->Create(NULL,WS_CHILD|WS_VISIBLE|BS_OWNERDRAW,rt,this,ID_BTN_PAGE0+i) ;rt.top = rt.bottom + 2 ;rt.bottom = rt.top + 30 ;}}//设置按钮背景图片及按钮的位置for(int i = 0; i < m_PropPageArray.size(); ++i){if(m_PropPageArray.at(i)->m_pPageBtn){m_PropPageArray.at(i)->m_pPageBtn->SetDlgBG(m_pDlgBGImage, m_PropPageArray.at(i)->m_rtArea);}}

下面看一下效果图:


原创粉丝点击