没有控件库,是种痛苦;用wince上现成的控件,更是种痛苦。能显示图片的按钮有木有?!木有。菜单能不能支持图标?不能!列表能不能漂亮点?咋就这样,爱用不用!想换个平台,没办法!公司指定非用wince不可,就是不愿跟上技术的步法,只能看着其他的公司happy的进行着ios,android的开发。程序猿(媛)们怎么办?
这也算是本人的痛苦经历了,怎么办?只能自绘呗!这次先说说自绘按钮的事吧。最终的效果如下图所示,其中左边的按钮是能够显示多种状态的图像,而右边的按钮是通过图像创建了圆形的形状,同时也能够显示多种状态的图像。此外图像和文字的对齐方式也是可以设置的。
首先需要从CButton派生一个子类,比如CImageButton。然后实现自绘,必须通知按钮“hi,你不要绘制了,这交给我了”!,这可以通过覆盖父类的虚函数PreSubclassWindow,在其实现体中调用ModifyStyle函数来通知。具体参见下面的代码。
void CImageButton::PreSubclassWindow() { CButton::PreSubclassWindow(); // add owner draw styles ModifyStyle(0, BS_OWNERDRAW); }
之后,每次需要绘制按钮的时候,都会调用虚函数virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct),因而所有的绘制工作都需要放在这个函数的实现当中,这也是实现自绘按钮的主要工作所在,必须根据按钮的当前状态、大小、对齐方式、文字和图像大小等等来绘制。
void CImageButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { ASSERT(lpDrawItemStruct != NULL); // must have at least the first bitmap loaded before calling DrawItem ASSERT(m_bitmapNorm.m_hObject != NULL); // required //Get button state BOOL isFocus = lpDrawItemStruct->itemState & ODS_FOCUS; BOOL isPush = lpDrawItemStruct->itemState & ODS_SELECTED; if (m_hMenu && m_isMenuDisplayed) { isPush = TRUE; } BOOL isDisable = lpDrawItemStruct->itemState & ODS_DISABLED; //Get button rect CRect btnRect(lpDrawItemStruct->rcItem); CRect frameControlRect(btnRect); int drawOffSet = 0; // get GDI resource: font,CDC,bitmap... CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CBitmap* pBitmap = &m_bitmapNorm; if (isDisable) { if (m_bitmapDisabled.m_hObject != NULL) { pBitmap = &m_bitmapDisabled; } } if (isPush) { if (m_bitmapSel.m_hObject != NULL) { pBitmap = &m_bitmapSel; } if (!m_isCreateRgnFromBitmap) { drawOffSet = 1; } } else if (isFocus) { if (m_bitmapFocus.m_hObject != NULL) { pBitmap = &m_bitmapFocus; } frameControlRect.DeflateRect(1,1); } // if don't use bitmap as rgn and disable, draw button face and frame rect if (!m_isCreateRgnFromBitmap && !isDisable) { // fill button face with default system button face color pDC->FillRect( btnRect , &CBrush( GetSysColor( COLOR_BTNFACE ) ) ); if (!isPush) { pDC->DrawFrameControl(frameControlRect,DFC_BUTTON,DFCS_BUTTONPUSH); } pDC->FrameRect(btnRect,CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); } // when button is disabled, if have not disable image,just fill image rect like text. // otherwise, use disabled image to fill this rect if (isDisable && m_bitmapDisabled.m_hObject == NULL) { //pDC->DrawState(CPoint(m_imageRect.left,m_imageRect.top),CSize(m_bitmap.bmWidth,m_bitmap.bmHeight),pBitmap,DST_BITMAP|DSS_DISABLED)); CRect imageRect(m_imageRect); imageRect.OffsetRect(1,1); pDC->FillRect(imageRect,&CBrush(GetSysColor(COLOR_WINDOW))); imageRect.OffsetRect(-1,-1); pDC->FillRect(imageRect,&CBrush(GetSysColor(COLOR_GRAYTEXT))); } else { CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap* pOld = memDC.SelectObject(pBitmap); if (pOld == NULL) return; CRect imageRect(m_imageRect); imageRect.OffsetRect(drawOffSet,0); pDC->BitBlt(imageRect.left, imageRect.top, imageRect.Width(), imageRect.Height(),&memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOld); memDC.DeleteDC(); } CString text; GetWindowText(text); if (!text.IsEmpty()) { if (isDisable) { CRect textRect(m_textRect); textRect.OffsetRect(1,1); pDC->SetTextColor(GetSysColor(COLOR_WINDOW)); int oldMode = pDC->SetBkMode(TRANSPARENT); CFont* pOldFont = pDC->SelectObject(GetTextFont()); pDC->DrawText(text,textRect,DT_WORDBREAK| DT_CENTER|DT_VCENTER); textRect.OffsetRect(-1,-1); pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); pDC->DrawText(text,textRect,DT_WORDBREAK | DT_CENTER|DT_VCENTER); if (pOldFont) { pDC->SelectObject(pOldFont); } pDC->SetBkMode(oldMode); } else { CRect textRect(m_textRect); textRect.OffsetRect(drawOffSet,0); pDC->SetTextColor(m_textColor); int oldMode = pDC->SetBkMode(TRANSPARENT); CFont* pOldFont = pDC->SelectObject(GetTextFont()); pDC->DrawText(text,textRect,DT_WORDBREAK | DT_CENTER|DT_VCENTER); if (pOldFont) { pDC->SelectObject(pOldFont); } pDC->SetBkMode(oldMode); } } if (!isDisable && isFocus && !m_isCreateRgnFromBitmap) { CRect focusRect(btnRect); focusRect.DeflateRect(2,2); focusRect.OffsetRect(drawOffSet,0); pDC->DrawFocusRect(focusRect); } }这当中有几个函数比较不常见, DrawFrameControl 、 FrameRect 、 DrawFocusRect, 调整相应的参数就可以看出其绘制的内容了。
自动调整大小,首先要计算出文字和图片所在空间大小,以及它们的对齐方式,之后就是简单的计算了。这里要强调的如何计算文字所占的空间大小,至于按照对齐方式计算可以查看源代码。
据了解,至少有2种方法可以计算出文字大小,这里介绍其中一种。这主要用到函数CDC::DrawText,代码如下。将其第三个参数设为DT_CALCRECT|DT_WORDBREAK,那么就可以通过第二个参数获得相应的文字大小。其中DT_CALCRECT表示此次调用不向设备输出(即不会真的绘制到屏幕上),只是计算文字大小;DT_WORDBREAK表示文字可自动换行。
CDC* pDC = GetDC(); if (pDC) { GetWindowText(text); textRect.SetRectEmpty(); if (!text.IsEmpty()) { CFont* pOldFont = pDC->SelectObject(GetTextFont()); pDC->DrawText(text,&textRect,DT_CALCRECT|DT_WORDBREAK); pDC->SelectObject(pOldFont); ReleaseDC(pDC); } }
创建不规则按钮
创建不规则按钮是通过掩码图片和颜色计算得出按钮的不规则区域,然后用新的区域来设置按钮的窗口区域及clip区域,详见下面的示例:
m_hClipRgn = CreateRgnFromBitmap(m_bitmapMask,RGB(255,255,255)); if (m_hClipRgn) { SetWindowRgn(m_hClipRgn,TRUE); CDC* pDC = GetDC(); if (pDC) { SelectClipRgn(pDC->GetSafeHdc(),m_hClipRgn); ReleaseDC(pDC); } }
计算区域的代码都放在了函数CreateRgnFromBitmap,这个是从codeproject的文章CxSkinButton里面“盗版”过来的,也没有细看。有兴趣的可以自己研究并分享下。
如何使用
// 图片按钮 m_imageButton.LoadBitmaps(IDB_INFO_BITMAP,IDB_INFO_PUSH_BITMAP,IDB_INFO_FOCUS_BITMAP); m_imageButton.SetAlignStyle(AS_IMAGE_LEFT_TEXT_RIGHT); m_imageButton.SizeToContent(); m_skinBtn.SizeToContent(); // 不规则按钮 m_skinBtn.LoadBitmaps(IDB_CIRCLE_NORM_BITMAP,IDB_CIRCLE_DOWN_BITMAP,IDB_CIRCLE_FOCUS_BITMAP,IDB_CIRCLE_DISABLE_BITMAP,IDB_CIRCLE_MASK_BITMAP); m_skinBtn.SizeToContent();
源文件下载地址:
http://download.csdn.net/detail/ryanzll/4518230