MFC界面美化方法

来源:互联网 发布:淘宝纠纷率影响大吗 编辑:程序博客网 时间:2024/04/29 09:13

MFC的界面真的是很难看,但是我们可以通过一些方法对它进行美化,看看一下的几个方法。

●重绘对话框

先看看效果图再上代码:


首先先在StdAfx.cpp的文件中加入代码:

class CBitmapEx:public CBitmap{protected:BITMAP m_bmpStruct;public:CBitmapEx(){memset(&m_bmpStruct,0,sizeof(BITMAP));} ;virtual~CBitmapEx(){} ;const int GetWidth()const{return m_bmpStruct.bmWidth;} ;const int GetHeight()const{return m_bmpStruct.bmHeight;} ;bool LoadBitmapEx(UINT nIDResource){if (CBitmap::LoadBitmap(nIDResource)){GetBitmap(&m_bmpStruct);return true;}return false;}};
这是自己写的继承CBitmap的类。

然后,创建几个位图的对象。

CBitmapEx m_bmpTopLeft;CBitmapEx m_bmpTopRepeat;CBitmapEx m_bmpTopRight;CBitmapEx m_bmpLeftRepeat;CBitmapEx m_bmpRightRepeat;CBitmapEx m_bmpBottomLeft;CBitmapEx m_bmpBottomRepeat;CBitmapEx m_bmpBottomRight;

在OnCreate函数中初始化这些对象,将准备好的位图和这些对象关联。

m_bmpTopLeft.LoadBitmapEx(DIALOG_TOP_LEFT); m_bmpTopRepeat.LoadBitmapEx(DIALOG_TOP_REPEAT);m_bmpTopRight.LoadBitmapEx(DIALOG_TOP_RIGHT);  m_bmpLeftRepeat.LoadBitmapEx(DIALOG_LEFT_REPEAT); m_bmpRightRepeat.LoadBitmapEx(DIALOG_RIGHT_REPEAT); m_bmpBottomLeft.LoadBitmapEx(DIALOG_BOTTOM_LEFT); m_bmpBottomRepeat.LoadBitmapEx(DIALOG_BOTTOM_REPEAT); m_bmpBottomRight.LoadBitmapEx(DIALOG_BOTTOM_RIGHT);
在OnNcPaint (WM_NCPAINT的消息映射,这个消息是用来处理frame的重绘的,具体的网址点击打开链接)中加入如下代码:

CWindowDC mdc(this);CDC   dcMemory;dcMemory.CreateCompatibleDC(&mdc);CRect rcWnd;GetWindowRect(&rcWnd);rcWnd.OffsetRect(-rcWnd.TopLeft());CRect  rcTopLeft(rcWnd);rcTopLeft.right=m_bmpTopLeft.GetWidth()+rcTopLeft.left;rcTopLeft.bottom=m_bmpTopLeft.GetHeight()+rcTopLeft.top;CBitmap *pOldBitmap=dcMemory.SelectObject(&m_bmpTopLeft);mdc.BitBlt(rcTopLeft.left,rcTopLeft.top,rcTopLeft.Width(),rcTopLeft.Height(),&dcMemory,0,0,SRCCOPY);CRect  rcTopRepeat(rcWnd);rcTopRepeat.left=rcTopLeft.right;rcTopRepeat.bottom=rcTopLeft.bottom;rcTopRepeat.right=rcWnd.right-m_bmpTopRight.GetWidth(); dcMemory.SelectObject(&m_bmpTopRepeat);mdc.StretchBlt(rcTopRepeat.left,rcTopRepeat.top,rcTopRepeat.Width(),rcTopRepeat.Height(),&dcMemory,0,0,m_bmpTopRepeat.GetWidth(),m_bmpTopRepeat.GetHeight(),SRCCOPY);CRect  rcTopRight(rcWnd);rcTopRight.left=rcTopRepeat.right;rcTopRight.bottom=rcTopRepeat.bottom;dcMemory.SelectObject(&m_bmpTopRight);mdc.BitBlt(rcTopRight.left,rcTopRight.top,rcTopRight.Width(),rcTopRight.Height(),&dcMemory,0,0,SRCCOPY);CRect  rcLeftRepeat;rcLeftRepeat.SetRect(rcWnd.left,rcTopLeft.bottom,m_bmpLeftRepeat.GetWidth(),rcWnd.bottom-m_bmpBottomLeft.GetHeight());dcMemory.SelectObject(&m_bmpLeftRepeat);mdc.StretchBlt(rcLeftRepeat.left,rcLeftRepeat.top,rcLeftRepeat.Width(),rcLeftRepeat.Height(),&dcMemory,0,0,m_bmpLeftRepeat.GetWidth(),m_bmpLeftRepeat.GetHeight(),SRCCOPY);CRect  rcRightRepeat(rcLeftRepeat);rcRightRepeat.left=rcTopRepeat.right;rcRightRepeat.right=rcWnd.right;dcMemory.SelectObject(&m_bmpRightRepeat);mdc.StretchBlt(rcRightRepeat.left,rcRightRepeat.top,rcRightRepeat.Width(),rcRightRepeat.Height(),&dcMemory,0,0,m_bmpRightRepeat.GetWidth(),m_bmpRightRepeat.GetHeight(),SRCCOPY);CRect  rcBottomLeft(rcWnd);rcBottomLeft.top=rcLeftRepeat.bottom;rcBottomLeft.right=rcLeftRepeat.right;dcMemory.SelectObject(&m_bmpBottomLeft);mdc.BitBlt(rcBottomLeft.left,rcBottomLeft.top,rcBottomLeft.Width(),rcBottomLeft.Height(),&dcMemory,0,0,SRCCOPY);CRect  rcBottomRepeat(rcWnd);rcBottomRepeat.left=rcBottomLeft.right;rcBottomRepeat.top=rcBottomLeft.top;rcBottomRepeat.right=rcWnd.right-m_bmpBottomRight.GetWidth();dcMemory.SelectObject(&m_bmpBottomRepeat);mdc.StretchBlt(rcBottomRepeat.left,rcBottomRepeat.top,rcBottomRepeat.Width(),rcBottomRepeat.Height(),&dcMemory,0,0,m_bmpBottomRepeat.GetWidth(),m_bmpBottomRepeat.GetHeight(),SRCCOPY);CRect  rcBottomRight(rcWnd);rcBottomRight.left=rcBottomRepeat.right;rcBottomRight.top=rcBottomRepeat.top;dcMemory.SelectObject(&m_bmpBottomRight);mdc.BitBlt(rcBottomRight.left,rcBottomRight.top,rcBottomRight.Width(),rcBottomRight.Height(),&dcMemory,0,0,SRCCOPY);dcMemory.SelectObject(pOldBitmap) ;

其实就是把图片重新画到frame上的过程。

然后在OnCtlColor(WM_CTLCOLOR的消息处理函数,这个消息是用来处理控件的颜色的,具体介绍的网址点击打开链接)中加入如下代码:

static CBrush brushDialog(RGB(240,240,240));if (nCtlColor == CTLCOLOR_DLG){return brushDialog;}if(nCtlColor == CTLCOLOR_STATIC){pDC->SetBkColor(RGB(240,240,240));HBRUSH m_bkBrush = ::CreateSolidBrush(RGB(240,240,240));        return m_bkBrush;}

上面的代码是用来重绘对话框上控件的颜色的。

再在OnEraseBkgnd中重绘对话框的背景:

CRect rcClient;CBrush brushBkgrnd(RGB(240,240,240));GetWindowRect(&rcClient);ScreenToClient(&rcClient);pDC->FillRect(&rcClient, &brushBkgrnd);

再在OnNcHitTest(WM_NCHITTEST发送到一个窗口,以确定窗口的一部分对应于一个特定的屏幕坐标,具体点击打开链接)中加入如下代码:

CPoint p(point) ;ScreenToClient(&p) ;if (p.y>=0&&p.y<=20)   return HTCAPTION   ;

这是指定窗口的一部分为非客户区,用来拖动窗口。

当然我们也可以用上面的方法来重写一个DLG的类,这样所有的对话框就都可以变成这种效果了。

就此就完成了对话框的重绘过程,上面需要的位图可以在这里下载。运用好这些消息处理,MFC也可以制作出很好看的对话框。

●重绘按钮

自己重绘一个按钮。

首先在PreSubclassWindow中 加入如下代码:

ModifyStyle(0, BS_OWNERDRAW);
允许进行按钮的重绘工作。

然后,创建一些我们需要使用的对象,代码如下:

        //按钮的外边框CPen m_BoundryPen;//鼠标指针置于按钮之上时按钮的内边框CPen m_InsideBoundryPenLeft;CPen m_InsideBoundryPenRight;CPen m_InsideBoundryPenTop;CPen m_InsideBoundryPenBottom;//按钮获得焦点时按钮的内边框CPen m_InsideBoundryPenLeftSel;CPen m_InsideBoundryPenRightSel;CPen m_InsideBoundryPenTopSel;CPen m_InsideBoundryPenBottomSel;//按钮的底色,包括有效和无效两种状态CBrush m_FillActive;CBrush m_FillInactive;//按钮的状态BOOL m_bOver;//鼠标位于按钮之上时该值为true,反之为flaseBOOL m_bTracking;//在鼠标按下没有释放时该值为trueBOOL m_bSelected;//按钮被按下是该值为trueBOOL m_bFocus;//按钮为当前焦点所在时该值为true
初始化这些对象,在构造函数中进行:

m_BoundryPen.CreatePen(PS_INSIDEFRAME | PS_SOLID, 1, RGB(0, 0, 0));m_InsideBoundryPenLeft.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(250, 196, 88)); m_InsideBoundryPenRight.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(251, 202, 106));m_InsideBoundryPenTop.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(252, 210, 121));m_InsideBoundryPenBottom.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(229, 151, 0));m_FillActive.CreateSolidBrush(RGB(223, 222, 236));m_FillInactive.CreateSolidBrush(RGB(222, 223, 236));m_InsideBoundryPenLeftSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(153, 198, 252)); m_InsideBoundryPenTopSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(162, 201, 255));m_InsideBoundryPenRightSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(162, 189, 252));m_InsideBoundryPenBottomSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(162, 201, 255));m_bOver = m_bSelected = m_bTracking = m_bFocus = FALSE;

重写绘制按钮底色的虚函数:

void CXPButton::DoGradientFill(CDC *pDC, CRect* rect){CBrush brBk[64];int nWidth = rect->Width();int nHeight = rect->Height();CRect rct;for (int i = 0; i < 64; i ++){if (m_bOver){if (m_bFocus)brBk[i].CreateSolidBrush(RGB(255 - (i / 4), 255 - (i / 4), 255 - (i / 3)));elsebrBk[i].CreateSolidBrush(RGB(255 - (i / 4), 255 - (i / 4), 255 - (i / 5)));}else{if (m_bFocus)brBk[i].CreateSolidBrush(RGB(255 - (i / 3), 255 - (i / 3), 255 - (i / 4)));elsebrBk[i].CreateSolidBrush(RGB(255 - (i / 3), 255 - (i / 3), 255 - (i / 5)));}}for (i = rect->top; i <= nHeight + 2; i ++) {rct.SetRect(rect->left, i, nWidth + 2, i + 1);pDC->FillRect(&rct, &brBk[((i * 63) / nHeight)]);}for (i = 0; i < 64; i ++)brBk[i].DeleteObject();}
重写绘制按钮内边框的函数:

void CXPButton::DrawInsideBorder(CDC *pDC, CRect* rect){CPen *pLeft, *pRight, *pTop, *pBottom;if (m_bSelected && !m_bOver){pLeft = & m_InsideBoundryPenLeftSel;pRight = &m_InsideBoundryPenRightSel;pTop = &m_InsideBoundryPenTopSel;pBottom = &m_InsideBoundryPenBottomSel;}else{pLeft = &m_InsideBoundryPenLeft;pRight = &m_InsideBoundryPenRight;pTop = &m_InsideBoundryPenTop;pBottom = &m_InsideBoundryPenBottom;}CPoint oldPoint = pDC->MoveTo(rect->left, rect->bottom - 1);CPen* pOldPen = pDC->SelectObject(pLeft);pDC->LineTo(rect->left, rect->top + 1);pDC->SelectObject(pRight);pDC->MoveTo(rect->right - 1, rect->bottom - 1);pDC->LineTo(rect->right - 1, rect->top);pDC->SelectObject(pTop);pDC->MoveTo(rect->left - 1, rect->top);pDC->LineTo(rect->right - 1, rect->top);pDC->SelectObject(pBottom);pDC->MoveTo(rect->left, rect->bottom);pDC->LineTo(rect->right - 1, rect->bottom);pDC->SelectObject(pOldPen);pDC->MoveTo(oldPoint);if (m_bSelected && !m_bOver)DrawFocusRect(pDC->m_hDC,rect);}

添加消息响应WM_MOUSEMOVE,并且根据鼠标移动函数添加鼠标经过和离开按钮的消息:

BEGIN_MESSAGE_MAP(CXPButton, CButton)//{{AFX_MSG_MAP(CXPButton)ON_WM_MOUSEMOVE()ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)//}}AFX_MSG_MAPEND_MESSAGE_MAP()
void CXPButton::OnMouseMove(UINT nFlags, CPoint point) {// TODO: Add your message handler code here and/or call defaultif (!m_bTracking){TRACKMOUSEEVENT tme;tme.cbSize = sizeof(tme);tme.hwndTrack = m_hWnd;tme.dwFlags = TME_LEAVE | TME_HOVER;tme.dwHoverTime = 1;m_bTracking = _TrackMouseEvent(&tme);}CButton::OnMouseMove(nFlags, point);}

分别处理各种情况,将标志的值设成相应的状态:
LRESULT CXPButton::OnMouseLeave(WPARAM wParam, LPARAM lParam){m_bOver = FALSE;m_bTracking = FALSE;InvalidateRect(NULL, FALSE);return 0;}LRESULT CXPButton::OnMouseHover(WPARAM wParam, LPARAM lParam){m_bOver = TRUE;InvalidateRect(NULL);return 0;}
最后在DrawItem中绘制按钮:

void CXPButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){//从lpDrawItemStruct获取控件的相关信息CRect rect =  lpDrawItemStruct->rcItem;CDC *pDC=CDC::FromHandle(lpDrawItemStruct->hDC);int nSaveDC=pDC->SaveDC();UINT state = lpDrawItemStruct->itemState;POINT pt ;TCHAR strText[MAX_PATH + 1];::GetWindowText(m_hWnd, strText, MAX_PATH);//画按钮的外边框,它是一个半径为5的圆角矩形pt.x = 5;pt.y = 5;CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);pDC->RoundRect(&rect, pt);//获取按钮的状态if (state & ODS_FOCUS){m_bFocus = TRUE;m_bSelected = TRUE;}else{m_bFocus = FALSE;m_bSelected = FALSE;}if (state & ODS_SELECTED || state & ODS_DEFAULT){m_bFocus = TRUE;}pDC->SelectObject(hOldPen);rect.DeflateRect(CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)));//根据按钮的状态填充按钮的底色CBrush* pOldBrush;if (m_bOver){pOldBrush = pDC->SelectObject(&m_FillActive);DoGradientFill(pDC, &rect);}else{pOldBrush = pDC->SelectObject(&m_FillInactive);DoGradientFill(pDC, &rect);}//根据按钮的状态绘制内边框if (m_bOver || m_bSelected)DrawInsideBorder(pDC, &rect);pDC->SelectObject(pOldBrush);//显示按钮的文本if (strText!=NULL){CFont* hFont = GetFont();CFont* hOldFont = pDC->SelectObject(hFont);CSize szExtent = pDC->GetTextExtent(strText, lstrlen(strText));CPoint pt( rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2);if (state & ODS_SELECTED) pt.Offset(1, 1);int nMode = pDC->SetBkMode(TRANSPARENT);if (state & ODS_DISABLED)pDC->DrawState(pt, szExtent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL);elsepDC->DrawState(pt, szExtent, strText, DSS_NORMAL, TRUE, 0, (HBRUSH)NULL);pDC->SelectObject(hOldFont);pDC->SetBkMode(nMode);}pDC->RestoreDC(nSaveDC);}

到此漂亮的按钮就制作完了,如果有想直接用的朋友,可以去这里下载。

●如果有用到PropertySheet的朋友,也可以用到上述的方法去改变界面的样式,但是有一些不同的地方。

去除属性页标题:

在sheet的OnInitDialog函数中,添加如下代码:

//去掉标题栏DWORD style =  GetStyle();style = style & ~WS_CAPTION;::SetWindowLong( GetSafeHwnd(), GWL_STYLE, style );

改变PropertyPage上的按钮,及和按钮在一行的标题栏的颜色:

需要我们去重写一个TabCtrl的类,并且在OnEraseBkgnd中加入如下代码:

CRect rect;GetWindowRect(&rect);ScreenToClient(&rect);//CBrush brush(GetSysColor(COLOR_3DFACE));// Tab Control背景色CBrush brush(RGB(240,240,240));pDC->FillRect(rect,&brush);return TRUE;

在DrawItem里加入如下代码:

CRect rect;GetWindowRect(&rect);ScreenToClient(&rect);//CBrush brush(GetSysColor(COLOR_3DFACE));// Tab Control背景色CBrush brush(RGB(240,240,240));pDC->FillRect(rect,&brush);return TRUE;}void COwnerDrawTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){TCHAR szTabText[64]={0};    TC_ITEM     tci;    tci.mask        = TCIF_TEXT;    tci.pszText     = szTabText;    tci.cchTextMax  = sizeof(szTabText)-1;CBrush m_brushBK(RGB(240,240,240));    GetItem( lpDrawItemStruct->itemID, &tci);    CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);        pDC->FillRect( &lpDrawItemStruct->rcItem, &m_brushBK);    pDC->SetBkColor( RGB(240,240,240));    if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&         (lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))    {        //Make the color of text of the selected tab to be BLUE.        pDC->SetTextColor(RGB( 0,0 , 255));    }    //! 文字的位置可能的偏移    pDC->TextOut(lpDrawItemStruct->rcItem.left+4,lpDrawItemStruct->rcItem.top+4,tci.pszText,lstrlen(tci.pszText));
最后在sheet的OnInitDialog中加入如下代码:

//更改TabCtrl颜色m_tabOwnerDraw.SubclassWindow( GetTabControl()->m_hWnd);    m_tabOwnerDraw.ModifyStyle(0,TCS_OWNERDRAWFIXED);

希望这些美化MFC界面的小经验对大家有用。





原创粉丝点击