导读:
我并不推荐采用自绘的方式去完成一些控件(比如 CStatic,CButton,RadioBox,CheckBox等)的美化,而是推荐大家从CWnd入手,把这些基本控件完全重新绘制一遍(当然, 有些做的很好的控件还是需要继承来自绘的,比如CListCtrl)。为什么这么做?因为MFC对这些控件的某些操作是隐蔽的,某些限制是我们无法接受的 (比如CTabCtrl的头部高度和每个Item的宽度)。我觉得掌握如下知识,绘制其他基本控件就不是绘制的问题,而是数据结构的事情了。
头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#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 |
我们需要的基本上就是这几个消息了。
实现文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
#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函数中接收到该控件的单击事件了。
发表评论
要发表评论,您必须先登录。