wince控件之自绘按钮(支持多种状态的图像加载、可通过图像创建不规程按钮)

没有控件库,是种痛苦;用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),因而所有的绘制工作都需要放在这个函数的实现当中,这也是实现自绘按钮的主要工作所在,必须根据按钮的当前状态、大小、对齐方式、文字和图像大小等等来绘制。

自绘细节
函数DrawItem的源码如下所示,主要包含如下几个部分:
  • DrawItem的参数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, 调整相应的参数就可以看出其绘制的内容了。

依据文字长度和图片大小自动调整按钮大小(SizeToContent)

自动调整大小,首先要计算出文字和图片所在空间大小,以及它们的对齐方式,之后就是简单的计算了。这里要强调的如何计算文字所占的空间大小,至于按照对齐方式计算可以查看源代码。
据了解,至少有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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值