MFC控件完全重繪從CWnd開始

導讀:

我並不推薦採用自繪的方式去完成一些控件(比如CStatic,CButton,RadioBox,CheckBox等)的美化,而是推薦大家從CWnd入手,把這些基本控件完全重新繪製一遍(當然,有些做的很好的控件還是需要繼承來自繪的,比如CListCtrl)。爲什麼這麼做?因爲MFC對這些控件的某些操作是隱蔽的,某些限制是我們無法接受的(比如CTabCtrl的頭部高度和每個Item的寬度)。我覺得掌握如下知識,繪製其他基本控件就不是繪製的問題,而是數據結構的事情了。

頭文件:

#ifndef QCTRL_H
#define QCTRL_H
#include <afxwin.h>

class QMemDC :	// 我把雙緩存封裝到類中,這樣就方便多了
	public CDC
{
private:
	CDC* dcSrc;
	CRect rect;
	CBitmap bmp;
public:
	QMemDC(CDC* dc,CRect rc);
	void Apply();
};

class QCtrl :
	public CWnd
{
protected:
	CString szClassName;
	bool isMouseIn;
	bool isPressed;
public:
	QCtrl();
	~QCtrl();
	bool Create(CWnd* pParent,CRect rc,CString text,DWORD id = 0,DWORD style = WS_VISIBLE|WS_CHILD);
protected:
	void PostClickEvent();
protected:
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg void OnMouseHover(UINT nFlags, CPoint point);
	afx_msg void OnMouseLeave();
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnPaint();
public:
	DECLARE_MESSAGE_MAP()
};

#endif

我們需要的基本上就是這幾個消息了。

實現文件:

#include "QCtrl.h"

// QMemDC

QMemDC::QMemDC(CDC* dc,CRect rc)
{
	dcSrc = dc;
	rect  = rc;
	// 創建內存DC
	CreateCompatibleDC(dc);
	bmp.CreateCompatibleBitmap(dc,rc.Width(),rc.Height());
	SelectObject(bmp);
}

void QMemDC::Apply()
{
	// 將內存DC繪製到設備DC上
	dcSrc->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),this,0,0,SRCCOPY);
}

// QCtrl

QCtrl::QCtrl()
{
	isMouseIn = false;
	isPressed = false;
	// 註冊控件類
	szClassName = AfxRegisterWndClass(0);
}

QCtrl::~QCtrl()
{

}

bool QCtrl::Create(CWnd* pParent,CRect rc,CString text,DWORD id /* = 0 */,DWORD style /* = WS_VISIBLE|WS_CHILD */)
{
	// 動態創建控件
	BOOL ret = CWnd::CreateEx(0,szClassName,text,style,rc,pParent,id);
	return ret ? true : false;
}

void QCtrl::PostClickEvent()
{
	// 該函數用來向父窗口發送 單擊 消息
	CWnd* parent = GetParent();
	if(parent != NULL)
	{
		WPARAM wp = MAKEWPARAM(GetDlgCtrlID(),BN_CLICKED);
		LPARAM lp = (LPARAM) m_hWnd;
		parent->PostMessage(WM_COMMAND,wp,lp);
	}
}

BEGIN_MESSAGE_MAP(QCtrl, CWnd)
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEHOVER()	// 此消息系統並不會給我們發送
	ON_WM_MOUSELEAVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

// 鼠標進入和鼠標移出消息需要我們自己監聽
void QCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
	// 只處理鼠標第一次進入時的情況
	if(!isMouseIn)
	{
		isMouseIn = true;

		TRACKMOUSEEVENT evt = { sizeof(evt), TME_LEAVE, m_hWnd, 0 };
		TrackMouseEvent(&evt);

		OnMouseHover(0,CPoint());
	}
}

void QCtrl::OnMouseHover(UINT nFlags, CPoint point)
{
	// 鼠標進入
	Invalidate();
}

void QCtrl::OnMouseLeave()
{
	// 鼠標離開
	isMouseIn = false;
	isPressed = false;
	Invalidate();
}

void QCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
	// 鼠標按下
	isPressed = true;
	Invalidate();
}

void QCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
	// 鼠標鬆開
	if(isPressed)
	{
		isPressed = false;
		Invalidate();

		PostClickEvent();
	}
}

BOOL QCtrl::OnEraseBkgnd(CDC* pDC)
{
	return TRUE;	// 阻止擦除背景,防止閃爍
}

void QCtrl::OnPaint()
{
	CPaintDC dc(this); 
	CRect rc;
	GetClientRect(&rc);
	// 採用雙緩存,防止閃爍
	QMemDC mdc(&dc,rc);
	// 刷背景
	COLORREF bkgnd = RGB(100,0,0);
	if(isMouseIn)
	{
		if(isPressed)
			bkgnd = RGB(250,0,0);
		else
			bkgnd = RGB(180,0,0);
	}
	mdc.FillSolidRect(&rc,bkgnd);
	// 設置文字字體
	CFont font;
	font.CreatePointFont(110,"宋體");	// 11號字體,該參數與實際字體號有10倍的關係
	mdc.SelectObject(font);
	// 獲取文字
	CString text;
	GetWindowText(text);
	// 設置文字屬性
	mdc.SetBkMode(TRANSPARENT);
	mdc.SetTextColor(RGB(0,0,0));
	// 繪製文本
	DWORD style = DT_SINGLELINE | DT_VCENTER | DT_CENTER;	// 文本格式:單行+水平居中+垂直居中
	mdc.DrawText(text,-1,&rc,style);	// 更多文本顯示格式可參考百度百科DrawText說明
	// 使繪製生效
	mdc.Apply();
}

如果上升到界面庫設計的高度,這裏的OnPaint函數應該這麼寫:

爲QCtrl添加一個虛函數virtual void DoPaint(QMemDC &dc,CRect rc);

CPaintDC dc(this);

CRect rc;

GetClientRect(&rc);// 採用雙緩存,防止閃爍

QMemDC mdc(&dc,rc);

DoPaint(mdc,rc); 

如此,子類繼承QCtrl只需要重寫該函數即可。

由於我們不是子類化,所以只能動態創建:

在CXXDlg.h添加變量QCtrl ctrl;

在OnInitDialog中ctrl.Create(this,CRect(10,10,210,30),"Nice Work");  //此處id和style是缺省參數,當我們指定一個ID後,就可以在CXXDlg的消息映射ON_BK_CLICKED函數中接收到該控件的單擊事件了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章