WM_DRAWITEM通告消息

参考文章:
白乔原创:VC之美化界面篇
CButtonST的源码
源码下载:
Demo Project
    当一个具有Owner-Draw风格的button control, combo-box control, list-box control, or menu需要显示外观时,会发送一条WM_DRAWITEM消息至它的隶属窗口。
    前面讲的WM_ERASEBKGND, WM_CTLCOLOR, NM_CUSTOMDRAW消息,只能对控件的外观进行小打小闹的修改。而WM_DRAWITEM消息则是Windows将控件的外观完全交由程序员绘制,所以可以完全发挥天马行空的Idea。
    WM_DRAWITEM消息为通告消息,所以可以通过消息反射机制交由控件类本身来处理,而MFC的CButton类,CComboBox类,CListBox类对WM_DRAWITEM反射消息交由虚函数DrawItem处理(可由CButton::OnChildNotify, CComboBox::OnChildNotify, CListBox::OnChildNotify源码看出)。所以,一般而言我们会override这些控件类的DrawItem函数实现对WM_DRAWITEM消息的处理。
    以下为通过WM_DRAWITEM消息自绘和标准MFC Push Button一模一样的按钮。下图中的Button1就是通过以下代码绘制的:
WM_DRAWITEM通告消息_第1张图片
    通过MFC类向导增加MFC Class: CMyButton,继承自CButton。
class CMyButton : public CButton
{
	DECLARE_DYNAMIC(CMyButton)

public:
	CMyButton();
	virtual ~CMyButton();

protected:
	DECLARE_MESSAGE_MAP()

protected:
	virtual void PreSubclassWindow();
	void DrawBorder(CDC *pDC, CRect *pRect);
public:
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
};
1.override PreSubclassWindow
将控件更改为Owner-Draw风格。
void CMyButton::PreSubclassWindow()
{
	// TODO: Add your specialized code here and/or call the base class
	ModifyStyle(BS_TYPEMASK, BS_OWNERDRAW, SWP_FRAMECHANGED);

	CButton::PreSubclassWindow();
}
2.override PreTranslateMessage
将鼠标双击消息解释为两次鼠标单击消息。
BOOL CMyButton::PreTranslateMessage(MSG* pMsg)
{
	// TODO: Add your specialized code here and/or call the base class
	if (pMsg->message == WM_LBUTTONDBLCLK)
		pMsg->message = WM_LBUTTONDOWN;

	return CButton::PreTranslateMessage(pMsg);
}
3.override DrawItem
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	//
	CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
	pDC->SetBkMode(TRANSPARENT);
	CRect rcBtn(lpDrawItemStruct->rcItem);

	//
	BOOL bIsFocused  = lpDrawItemStruct->itemState & ODS_FOCUS;
	BOOL bIsPressed  = lpDrawItemStruct->itemState & ODS_SELECTED;
	BOOL bIsDisabled = lpDrawItemStruct->itemState & ODS_DISABLED;

	// Draw background
	CBrush brushBk(::GetSysColor(COLOR_BTNFACE));
	if (bIsFocused)
	{
		rcBtn.DeflateRect(1, 1);
		pDC->FillRect(&rcBtn, &brushBk);
		rcBtn.DeflateRect(-1, -1);

		CBrush brushFrame(RGB(0, 0, 0));
		pDC->FrameRect(&rcBtn, &brushFrame);
	}
	else
	{
		pDC->FillRect(&rcBtn, &brushBk);
	}


	// Draw border
	if (bIsPressed)
	{
		rcBtn.DeflateRect(1, 1);
		CBrush brBtnShadow(::GetSysColor(COLOR_BTNSHADOW));
		pDC->FrameRect(&rcBtn, &brBtnShadow);
		rcBtn.DeflateRect(-1, -1);
	}
	else if (bIsFocused)
	{
		rcBtn.DeflateRect(1, 1);
		DrawBorder(pDC, &rcBtn);
		rcBtn.DeflateRect(-1, -1);
	}
	else
	{
		DrawBorder(pDC, &rcBtn);
	}

	//// Draw focus rect
	if (bIsFocused)
	{
		rcBtn.DeflateRect(4, 4);
		pDC->DrawFocusRect(&rcBtn);
		rcBtn.DeflateRect(-4, -4);
	}

	// Draw text
	CString strText;
	GetWindowText(strText);
	CRect rcText(rcBtn);
	if (bIsPressed)
	{
		rcText.left += 2;
	}
	else
	{
		rcText.bottom -= 2;
	}
	if (bIsDisabled)
	{
		rcText.OffsetRect(1, 1);
		pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
		pDC->DrawText(strText, rcText, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
		rcText.OffsetRect(-1, -1);
		pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
		pDC->DrawText(strText, rcText, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
	}
	else
	{
		pDC->DrawText(strText, &rcText, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
	}
}

void CMyButton::DrawBorder(CDC *pDC, CRect *pRect)
{
	CPen penBtnHiLight(PS_SOLID, 0, GetSysColor(COLOR_BTNHILIGHT)); // White
	CPen pen3DLight(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));       // Light gray
	CPen penBtnShadow(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));   // Dark gray
	CPen pen3DDKShadow(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW)); // Black
	// Draw top-left borders
	// White line
	CPen* pOldPen = pDC->SelectObject(&penBtnHiLight);
	pDC->MoveTo(pRect->left, pRect->bottom-1);
	pDC->LineTo(pRect->left, pRect->top);
	pDC->LineTo(pRect->right, pRect->top);
	// Light gray line
	pDC->SelectObject(pen3DLight);
	pDC->MoveTo(pRect->left+1, pRect->bottom-1);
	pDC->LineTo(pRect->left+1, pRect->top+1);
	pDC->LineTo(pRect->right, pRect->top+1);
	// Draw bottom-right borders
	// Black line
	pDC->SelectObject(pen3DDKShadow);
	pDC->MoveTo(pRect->left, pRect->bottom-1);
	pDC->LineTo(pRect->right-1, pRect->bottom-1);
	pDC->LineTo(pRect->right-1, pRect->top-1);
	// Dark gray line
	pDC->SelectObject(penBtnShadow);
	pDC->MoveTo(pRect->left+1, pRect->bottom-2);
	pDC->LineTo(pRect->right-2, pRect->bottom-2);
	pDC->LineTo(pRect->right-2, pRect->top);
	//
	pDC->SelectObject(pOldPen);
}
解释:
9~11行 对于PushButton而言,有四种状态:Normal, Disabled, Focused, Pressed.       
       Normal状态下,lpDrawItemStruct->itemState = 0x0000.       
       Disabled状态下,lpDrawItemStruct->itemState = ODS_DISABLED.       
       Focused状态下,lpDrawItemStruct->itemState = ODS_FOCUS.       
       Pressed状态下,lpDrawItemStruct->itemState = ODS_FOCUS | ODS_SELECTED.       
       因为按下按钮肯定是拥有焦点。

13~27行 绘制按钮的背景        
        如果按钮拥有焦点,先在按钮大小上下左右各收缩1的矩形内刷背景(17~19行),再在按钮外围绘制黑色矩形边框(21~22行)。        
        如果按钮不拥有焦点,则在按钮大小范围内刷背景(26行)。

30~47行 绘制外围边框        
        如果按钮被按下,则在按钮大小上下左右各收缩1的矩形上绘制灰色边框。        
        如果按钮拥有焦点,则在按钮大小上下左右各收缩1的矩形上绘制3D边框,具体见函数DrawBorder。        
        否则,则在按钮大小矩形上绘制3D边框,具体见函数DrawBorder。

49~55行 绘制焦点矩形,在按钮大小上下左右各收缩4的矩形上绘制。

57~81行 绘制按钮Caption        
        67行意在使Caption在垂直方向上居中。        
        63行:如果按钮被按下,则Caption相对于正常状态下向左及下各偏1。(67行未执行则保证向下偏移1)        
        69~77行:对Disabled按钮,使Caption呈阴影灰色状态。        
        80行:绘制Caption.

你可能感兴趣的:(WM_DRAWITEM通告消息)