在描绘MFC界面时,MFC自带的控件样式是绝对不满足界面的需求的。
所以我们就要在MFC自带控件基础上对控件样式进行重绘。
在采用自绘前界面样式
采用自绘后界面样式
是不是自绘控件后看起来正常了很多?
自绘控件的步骤:
我们以做一个关闭按钮为例
- 先创建一个MFC类继承自CButton。
- 给这个类添加 一个虚函数DrawItem(),一个虚函数PreSubclassWindow()和 一个afx BOOL OnEraseBkgnd()函数(一般以afx开头的函数都会在消息映射里面有一条映射)
-
DrawItem()是控件的自绘处理函数,在这个函数中可以对控件的样式进行描绘。
附一段代码为例:
1 void CDhsButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 2 { 3 CDC* pDC= CDC::FromHandle(lpDrawItemStruct->hDC); 4 CRect rect = &lpDrawItemStruct->rcItem; 5 UINT uID = lpDrawItemStruct->CtlID; 6 7 Graphics g(pDC->m_hDC); 8 g.SetSmoothingMode(SmoothingModeHighQuality); 9 10 11 if (::GetWindowLong(m_hWnd, GWL_STYLE) & WS_DISABLED) 12 { 13 if (m_pImageDisable != NULL) 14 g.DrawImage(m_pImageDisable, 0, 0, rect.Width(), rect.Height()); 15 else 16 g.DrawImage(m_pImageNormal, 0, 0, rect.Width(), rect.Height()); 17 } 18 else if (m_nMouseState == Down) 19 { 20 if (m_pImageOver != NULL) 21 g.DrawImage(m_pImageOver, 0, 0, rect.Width(), rect.Height()); 22 else 23 g.DrawImage(m_pImageNormal, 0, 0, rect.Width(), rect.Height()); 24 } 25 else if (m_bSelected) 26 { 27 if (m_pImageSelected != NULL) 28 g.DrawImage(m_pImageSelected, 0, 0, rect.Width(), rect.Height()); 29 else 30 g.DrawImage(m_pImageNormal, 0, 0, rect.Width(), rect.Height()); 31 } 32 else 33 { 34 g.DrawImage(m_pImageNormal, 0, 0, rect.Width(), rect.Height()); 35 } 36 37 if (!m_strCaption.IsEmpty()) 38 { 39 rect.left += 5; 40 41 if (::GetWindowLong(m_hWnd, GWL_STYLE) & WS_DISABLED) 42 { 43 PublicFun::GDIDrawText(pDC, m_strCaption, rect, m_pFont, RGB(120, 120, 120), FALSE, TRUE); 44 } 45 else if (m_bSelected) 46 { 47 PublicFun::GDIDrawText(pDC, m_strCaption, rect, m_pFont, RGB(0, 0, 0), FALSE, TRUE); 48 } 49 else if (m_nMouseState == Over) 50 { 51 PublicFun::GDIDrawText(pDC, m_strCaption, rect, m_pFont, RGB(0, 0, 0), FALSE, TRUE); 52 } 53 else 54 { 55 PublicFun::GDIDrawText(pDC, m_strCaption, rect, m_pFont, RGB(0, 0, 0), FALSE, TRUE); 56 } 57 } 58 59 ReleaseDC(pDC); 60 }
-
添加OnEraseBkgnd()函数代码,一般都是固定的
BOOL CDhsButton::OnEraseBkgnd(CDC* pDC) { return TRUE; }
-
添加虚函数PreSubclassWindow函数代码(PreSubclassWindow函数实际上是在CWnd::CeateEx方法中的 AfxHookWindowCreate(this)方法中实现的,AfxHookWindowCreate作用是设置钩子函数,所以你如果想在创建窗口之前将窗口与自己的派生类进行关联,这时候建立前的处理就要在PreSubclassWindow中写。)
具体来说,
- 如果你定义一个窗口(如CButton派生类CMyButton),然后使用对话框数据交换比如通过DDX将一个按钮与自己的派生类对象关联,这时候,一些"建立前"的处理就应该写在"PreSubclassWindow"中。
- 如果你用的不是"对话框数据关联",而是在OnInitDialg中自己创建.这时候,一些"建立前"的处理就应该写在 "PreCreateWindow"中。)
在PreSubclassWindow函数中,设置ModifyStyle(0, BS_OWNERDRAW);
代码如下
1 void CDhsButton::PreSubclassWindow() 2 { 3 ModifyStyle(0, BS_OWNERDRAW); 4 5 CButton::PreSubclassWindow(); 6 }
使用BS_OWNERDRAW属性 是要求创建CButton的继承类,并在其中重载DrawItem方法才可以。你要是不想改变Button的外观不要用这个属性。
意思就是如果你要重载派生类按钮中的DrawItem方法,必须要设置了BS_OWNERDRAW 才能重载
6.因为是通过DDX关联的方式,所以在使用上,要用DoDataExchange方法将派生类与资源中的按钮进行关联。
7.添加按钮事件: