在这里我首先创建了一个CColorButton类,继承自CButton,要实现自绘,首先是要加入BS_OWNERDRAW样式
BOOL CColorButton::PreCreateWindow(CREATESTRUCT& cs) { BOOL bRet=CButton::PreCreateWindow(cs); ColorButtonInit(); return bRet; } void CColorButton::PreSubclassWindow() { CButton::PreSubclassWindow(); ColorButtonInit(); } void CColorButton::ColorButtonInit() { m_bTracking=false; m_bOver=m_bDown=m_bDisable=false; m_bDisable=IsWindowEnabled()?FALSE:TRUE; ModifyStyle(NULL,BS_OWNERDRAW); }
然后重载DrawItem函数:
void CColorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { DrawButton(lpDrawItemStruct->hDC); }
这里的DrawButton是一个单独的函数,用来画出按钮,这里我使用了DrawThemeBackground来画按钮背景,这个API函数的优点是可以根据当前系统主题来画出控件:
void CColorButton::DrawButton(HDC hDestDC) { CRect rc; GetClientRect(rc); int nWindth=rc.Width(); int nHeight=rc.Height(); HDC hDC=CreateCompatibleDC(hDestDC);//创建兼容DC,采用双缓冲画出 HBITMAP hBitmap=CreateCompatibleBitmap(hDestDC,nWindth,nHeight); HBITMAP hOldBitmap=(HBITMAP)SelectObject(hDC,hBitmap); //画出整个控件背景 HBRUSH hbr=CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); FillRect(hDC,&rc,hbr); DeleteObject(hbr); HTHEME hTheme=OpenThemeData(m_hWnd,L"Button"); if(hTheme){ if(m_bDisable){//禁止状态 DrawThemeBackground (hTheme,hDC, BP_PUSHBUTTON, PBS_DISABLED,&rc,NULL); }else if(m_bDown){//按下状态 DrawThemeBackground (hTheme,hDC, BP_PUSHBUTTON, PBS_PRESSED,&rc,NULL); }else if(m_bOver){//热点状态 DrawThemeBackground (hTheme,hDC, BP_PUSHBUTTON, PBS_HOT,&rc,NULL); }else{//普通状态 DrawThemeBackground (hTheme,hDC, BP_PUSHBUTTON, PBS_NORMAL,&rc,NULL); } CloseThemeData (hTheme); }else{ if(m_bDisable){ DrawFrameControl (hDC,rc,DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_INACTIVE); }else if(m_bDown){ DrawFrameControl (hDC,rc,DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_PUSHED); }else if(m_bOver){ DrawFrameControl (hDC,rc,DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_HOT); }else{ DrawFrameControl (hDC,rc,DFC_BUTTON, DFCS_BUTTONPUSH); } } //画出颜色显示区 rc=CRect(4,4,nWindth-17,nHeight-4); hbr=CreateSolidBrush(0xFFFFFF);//颜色显示区的背景 FillRect(hDC,&rc,hbr); DeleteObject(hbr); HPEN hPen=CreatePen(PS_SOLID,1,GetSysColor(COLOR_WINDOWFRAME));//颜色显示区的边框 FrameRect(hDC,&rc,(HBRUSH)hPen); DeleteObject(hPen); rc.InflateRect(-2,-2); hbr=CreateSolidBrush(m_CurColor);//当前的颜色 FillRect(hDC,&rc,hbr); DeleteObject(hbr); //画出下拉的小三角 int w=7; int h=4; int x=nWindth-w-7; int y=(nHeight-h)/2; for(int i=0;i<h;i++){ MoveToEx(hDC,x,y,NULL); LineTo(hDC,x+w,y); x++; y++; w=w-2; } //复制到控件的DC上 BitBlt(hDestDC,0,0,nWindth,nHeight,hDC,0,0,SRCCOPY); //删除资源,释放内存 SelectObject(hDC,hOldBitmap); DeleteObject(hBitmap); DeleteDC(hDC); }
当然还需要在按钮的鼠标消息里记录按钮的状态,但这些不是本例程的主要部分,在文章里就不全部写出了,大家可以下载完整源码查看。
最后响应BN_CLICKED消息,弹出位于下方的颜色选择控件。
这个颜色选择控件稍微复杂一些,也可以算作本例程里一些有用的东西,我是继承自CWnd创建了一个新的窗口类,采用DirectUI的方式画出各个颜色小方块,即各个“子控件”事实上是不存在的,只是一些逻辑区域,直接画在父窗口上。
首先我定义了一个数据结构来保存各个“子控件”的信息,并且加入到数组
typedef struct tagCOLORITEM { COLORREF color; RECT rect; BOOL bCheck; }COLORITEM,*LPCOLORITEM; CArray <COLORITEM,COLORITEM&> m_ItemArray;
创建完窗口后创建各个“子控件”:
CreateItem(0x000000,_T("黑色")); CreateItem(0x800000,_T("藏青")); int CColorWnd::CreateItem(COLORREF color,CString strName) { int nIndex=m_nCount; COLORITEM item; item.bCheck=color==m_CurColor; item.color=color; item.rect=CRect(m_nItemX,m_nItemY,m_nItemX+18,m_nItemY+18); m_ItemArray.Add(item); m_nCount++; m_nItemX+=18; if(m_nCount%7==0){ m_nItemY+=18; m_nItemX=0; } m_ToolTip.AddTool(this,strName,&item.rect,100+nIndex); return nIndex; }
最后画出各个“子控件”:
void CColorWnd::UpdateCache() { CRect rc; GetClientRect(rc); int nWindth=rc.Width(); int nHeight=rc.Height(); HDC hDC=::GetDC(m_hWnd); m_hCacheDC=CreateCompatibleDC(hDC);//创建兼容DC,采用双缓冲画出 m_hCacheBitmap=CreateCompatibleBitmap(hDC,nWindth,nHeight); m_hOldBitmap=(HBITMAP)SelectObject(m_hCacheDC,m_hCacheBitmap); m_hOldFont=(HFONT)SelectObject(m_hCacheDC,(HFONT)GetStockObject(DEFAULT_GUI_FONT)); SetBkMode(m_hCacheDC,TRANSPARENT); m_hOrderPen1=CreatePen(PS_SOLID,1,0xA0A0A0); m_hOrderPen2=CreatePen(PS_SOLID,1,0xFFFFFF); m_hBackBrush1=CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); m_hBackBrush2=CreateSolidBrush(0xFFFFFF); FillRect(m_hCacheDC,&rc,m_hBackBrush1); int x=4; int y=nHeight-29; HPEN hOldPen=(HPEN)SelectObject(m_hCacheDC,m_hOrderPen1); MoveToEx(m_hCacheDC,x,y,NULL); LineTo(m_hCacheDC,nWindth-x,y); SelectObject(m_hCacheDC,hOldPen); y+=1; hOldPen=(HPEN)SelectObject(m_hCacheDC,m_hOrderPen2); MoveToEx(m_hCacheDC,x,y,NULL); LineTo(m_hCacheDC,nWindth-x,y); SelectObject(m_hCacheDC,hOldPen); for(int i=0;i<m_nCount;i++){ DrawItem(m_hCacheDC,i); } BitBlt(hDC,0,0,nWindth,nHeight,m_hCacheDC,0,0,SRCCOPY); ::ReleaseDC(m_hWnd,hDC); } void CColorWnd::DrawItem(HDC hDC,int nIndex) { COLORITEM item=m_ItemArray.GetAt(nIndex); HBRUSH hbr=m_hBackBrush1; if(item.color!=-1 && item.bCheck){ hbr=m_hBackBrush2; } FillRect(m_hCacheDC,&item.rect,hbr); if(m_nDownItem==nIndex || item.bCheck){ HPEN hOldPen=(HPEN)SelectObject(m_hCacheDC,m_hOrderPen1); MoveToEx(m_hCacheDC,item.rect.left,item.rect.top,NULL); LineTo(m_hCacheDC,item.rect.right-1,item.rect.top); MoveToEx(m_hCacheDC,item.rect.left,item.rect.top,NULL); LineTo(m_hCacheDC,item.rect.left,item.rect.bottom-1); SelectObject(m_hCacheDC,hOldPen); hOldPen=(HPEN)SelectObject(m_hCacheDC,m_hOrderPen2); MoveToEx(m_hCacheDC,item.rect.right-1,item.rect.top,NULL); LineTo(m_hCacheDC,item.rect.right-1,item.rect.bottom-1); MoveToEx(m_hCacheDC,item.rect.left,item.rect.bottom-1,NULL); LineTo(m_hCacheDC,item.rect.right-1,item.rect.bottom-1); SelectObject(m_hCacheDC,hOldPen); }else if(m_nHotItem==nIndex){ HPEN hOldPen=(HPEN)SelectObject(m_hCacheDC,m_hOrderPen2); MoveToEx(m_hCacheDC,item.rect.left,item.rect.top,NULL); LineTo(m_hCacheDC,item.rect.right-1,item.rect.top); MoveToEx(m_hCacheDC,item.rect.left,item.rect.top,NULL); LineTo(m_hCacheDC,item.rect.left,item.rect.bottom-1); SelectObject(m_hCacheDC,hOldPen); hOldPen=(HPEN)SelectObject(m_hCacheDC,m_hOrderPen1); MoveToEx(m_hCacheDC,item.rect.right-1,item.rect.top,NULL); LineTo(m_hCacheDC,item.rect.right-1,item.rect.bottom-1); MoveToEx(m_hCacheDC,item.rect.left,item.rect.bottom-1,NULL); LineTo(m_hCacheDC,item.rect.right-1,item.rect.bottom-1); SelectObject(m_hCacheDC,hOldPen); } if(item.color!=-1){ CRect rc(item.rect); rc.InflateRect(-3,-3); hbr=CreateSolidBrush(item.color); FillRect(hDC,&rc,hbr); DeleteObject(hbr); FrameRect(hDC,&rc,(HBRUSH)m_hOrderPen1); }else{ DrawText(hDC,_T("其他颜色..."),-1,&item.rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE); } }
这部分,我就没有自己去画了,而是直接采用了MFC的CColorDialog:
CColorDialog dlg(m_CurColor,0,this); if(dlg.DoModal()==IDOK){ SetColor(dlg.GetColor()); }
1.把ColorButton.h、ColorButton.cpp加入到你的工程
2.在对话框的头文件里引用ColorButton.h
#include "ColorButton.h"
3.在对话框的头文件里声明变量
CColorButton m_ColorButton1;
4.可以使用一下3种方式来关联按钮控件
DDX_Control(pDX,IDC_BUTTON1, m_ColorButton1);
m_ColorButton1.SubclassWindow(...)子类化关联现有控件;
m_ColorButton1.Create(...)创建控件
5.在BEGIN_MESSAGE_MAP里添加消息映射
ON_BN_COLORCHANGE(IDC_BUTTON1,& CColorBtnTestDlg::OnBnColorChangeButton1)
6.设置“颜色选择器”的颜色 m_ColorButton1.SetColor()
7.获取“颜色选择器”的当前选择的颜色 m_ColorButton1.GetColor()
很多时候我们需要一些非系统自带的标准控件,完全可以通过自绘来实现,Windows整个都是画出来的,只要掌握了方法,我们也可以画出各种的窗口、控件,希望本文能给你带来一些帮助。