MFC控件完全重绘从CWnd开始

来源:互联网 发布:手机股票预警软件 编辑:程序博客网 时间:2024/04/28 06:40

导读:

我并不推荐采用自绘的方式去完成一些控件(比如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"// QMemDCQMemDC::QMemDC(CDC* dc,CRect rc){dcSrc = dc;rect  = rc;// 创建内存DCCreateCompatibleDC(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);}// QCtrlQCtrl::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);elsebkgnd = 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函数中接收到该控件的单击事件了。