摘要:本文主要向大家介绍了VC编程按钮自绘的简单实现,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。
本文主要向大家介绍了VC编程按钮自绘的简单实现,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。
之前一直使用公司开发的界面库来进行VC界面设计,今天偶然发现其中一个按钮类不能实现特定需求,例如文字的位置不能随意显示在按钮上。于是唯有重新定义了该按钮类的自绘功能,添加新的属性和操作。
通过这次修改,感觉控件自绘也还是蛮有意思的,本来不能实现的功能,通过类的派生和自绘就能实现,感觉真爽,符合人性欲望无限扩展的本性。
说起VC的界面设计,大家都应该很清楚,MFC提供了很多标准的控件,例如按钮,下拉框等等,但是这些控件本身并不美观。要想改变它们的形状和外观,就必须要进行控件的自绘。我们常常看到QQ,360的界面设计,之所以那么狂吊炸天,就是因为他们有自己的界面库,能实现控件的自绘。现以一个简单的例子来介绍按钮自绘的方法实现。(部分函数代码和实现原理还是要列出的,因为代码才是王道啊,步骤就略过哈。)
1. 功能需求
1) 实现按钮文字任意位置的偏移。效果图如下:
2) 实现按钮的几种状态
Normal:
Over:
Down:
Disable:
2. 方法实现
首先定义一个CSkinButton类,它继承于CButton类。
1) 自绘属性
要想实现按钮自绘,需要把按钮风格修改为自绘属性BS_OWNERDRAW,从而系统会发送WM_DRAWITEM消息给CButton类,才会调用重载函数DrawItem实现自绘。可选择在对话框属性中或者在重载函数PreSubclassWindow中添加ModifyStyle(0,BS_OWNERDRAW);。
void CSkinButton::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class ModifyStyle(0,BS_OWNERDRAW); CButton::PreSubclassWindow();}
2) 重载DrawItem()函数
DrawItem()函数主要是根据按钮的状态来绘制自身的形状和外观,按钮控件自绘的功能就是在这里实现的。它主要包含了一个LPDRAWITEMSTRUCT的指针。声明如下:
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType; //控件的类型UINT CtlID; //自绘控件IDUINT itemID; //菜单项IDUINT itemAction; //绘制行为UINT itemState; //当前绘制操作完成后,所绘项的可见状态HWND hwndItem; //指定了组合框、列表框和按钮等自绘控件的窗口句柄HDC hDC; //绘制操作所使用的设备环境RECT rcItem; //绘制的矩形区域。这个区域就是上面hDC的作用范围。ULONG_PTR itemData; //菜单项数据} DRAWITEMSTRUCT, NEAR *PDRAWITEMSTRUCT, FAR *LPDRAWITEMSTRUCT
具体实现如下:
void CSkinButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{ // TODO: Add your code to draw the specified item
CRect rcItem(lpDrawItemStruct->rcItem);
CString strText,strPath;
CString strWorkPath; //按钮状态
int nStatus = CONTROL_STATUS_NORMAL; if ( !IsWindowEnabled() )
{
nStatus = CONTROL_STATUS_DISABLE;
} else
{ if ( m_bTrackMouseEvent )
{ if ( m_bLButtonDown )
{
nStatus = CONTROL_STATUS_DOWN;
} else
{
nStatus = CONTROL_STATUS_OVER;
}
} else
{// if ( ::GetFocus()==m_hWnd )// {// nStatus = CONTROL_STATUS_FOCUS;// }// else
{
nStatus = CONTROL_STATUS_DISABLE/*CONTROL_STATUS_NORMAL*/;
}
}
} if ( m_hBitmap[nStatus] )
{ //绘图
Draw9gridTransparent(lpDrawItemStruct->hDC, lpDrawItemStruct->rcItem, m_rcBorder9grid, m_hBitmap[nStatus]);
} //使用透明的输出,也就是文字的背景是不改变的
::SetBkMode(lpDrawItemStruct->hDC, TRANSPARENT);
GetWindowText(strText); if (m_unDrawTextStyle==BTN_DRAW_TEXT_CENTER)
{
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), rcItem, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
} else if (m_unDrawTextStyle==BTN_DRAW_TEXT_LEFT)
{ //根据偏移值来绘制
rcItem.left = rcItem.left+m_unDrawTextSize;
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), rcItem, DT_LEFT|DT_VCENTER|DT_SINGLELINE);
} else if (m_unDrawTextStyle==BTN_DRAW_TEXT_RIGHT)
{
rcItem.right = rcItem.right-m_unDrawTextSize;
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), rcItem, DT_RIGHT|DT_VCENTER|DT_SINGLELINE);
}
}
3) 文字绘制
文字的绘制使用DrawText函数,偏移值只要根据矩形区域的left和right来设置就行了,具体可参看DrawItem函数:
//根据偏移值来绘制
rcItem.right = rcItem.right-m_unDrawTextSize;
::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), rcItem, DT_RIGHT|DT_VCENTER|DT_SINGLELINE);1234
4) Mouse相关响应
想要按钮随着鼠标移动来动态显示状态,需要触发WM_MOUSEMOVE,WM_MOUSELEAVE和WM_MOUSEHOVER消息。由于WM_MOUSEMOVE消息是标准的Windows消息,只需要在类向导中添加即可触发,而WM_MOUSELEAVE和WM_MOUSEHOVER两个消息需要通过WM_MOUSEMOVE消息来启动触发。
OnMouseLeave实现定义如下://CSkinButton.h定义
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);//CSkinButton.cpp定义消息
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)//CSkinButton.cpp实现
LRESULT CSkinButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{if ( m_bTrackMouseEvent )
{
m_bTrackMouseEvent = FALSE;
Invalidate();
}
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); return 1;
}OnMouseMove实现如下:void CSkinButton::OnMouseMove(UINT nFlags, CPoint point) {
// TODO: Add your message handler code here and/or call default
CRect rect; GetWindowRect(rect); ScreenToClient(rect); if ( !m_bTrackMouseEvent )
{ m_bTrackMouseEvent = TRUE; TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.dwHoverTime = HOVER_DEFAULT; tme.hwndTrack = m_hWnd; _TrackMouseEvent(&tme); Invalidate();
}
::SetCursor(AfxGetApp()->LoadStandardCursor(MAKEINTRESOURCE(32649))); CButton::OnMouseMove(nFlags, point);
}
5) 图片绘制
装载位图:
HBITMAP hImageBitmap = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),strImagePath, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE);按九个方向进行图片绘制:
BOOL CSkinButton::Draw9gridTransparent(HDC hSrcDC, CRect rcItem, CRect rcBorder, HBITMAP hBitmap)
{
if (hBitmap)
{
BITMAP bitmap;
::GetObject(hBitmap, sizeof(BITMAP), &bitmap);
int m_nWidth = bitmap.bmWidth;
int m_nHeight = bitmap.bmHeight;
HDC memdc;
memdc = CreateCompatibleDC(hSrcDC);
::SelectObject(memdc, hBitmap);
//左上角
CRect rcLeftTop;
rcLeftTop.left = rcItem.left;
rcLeftTop.top = rcItem.top;
rcLeftTop.right = rcLeftTop.left+m_rcBorder9grid.left;
rcLeftTop.bottom = rcLeftTop.top+m_rcBorder9grid.top;
::TransparentBlt(hSrcDC, rcLeftTop.left, rcLeftTop.top, rcLeftTop.Width(), rcLeftTop.Height(), memdc, 0, 0, m_rcBorder9grid.left, m_rcBorder9grid.top, RGB(255,0,255));
//右上角
CRect rcRightTop;
rcRightTop.left = rcItem.right-m_rcBorder9grid.right;
rcRightTop.right = rcItem.right;
rcRightTop.top = rcItem.top;
rcRightTop.bottom = rcRightTop.top+m_rcBorder9grid.top;
::TransparentBlt(hSrcDC, rcRightTop.left, rcRightTop.top, rcRightTop.Width(), rcRightTop.Height(), memdc, m_nWidth-m_rcBorder9grid.right, 0, m_rcBorder9grid.right, m_rcBorder9grid.top, RGB(255,0,255));
//右下角
CRect rcRightBottom;
rcRightBottom.left = rcItem.right-m_rcBorder9grid.right;
rcRightBottom.right = rcItem.right;
rcRightBottom.bottom= rcItem.bottom;
rcRightBottom.top = rcRightBottom.bottom-m_rcBorder9grid.bottom;
::TransparentBlt(hSrcDC, rcRightBottom.left, rcRightBottom.top, rcRightBottom.Width(), rcRightBottom.Height(), memdc, m_nWidth-m_rcBorder9grid.right, m_nHeight-m_rcBorder9grid.bottom, m_rcBorder9grid.right, m_rcBorder9grid.bottom, RGB(255,0,255));
//左下角
CRect rcLeftBottom;
rcLeftBottom.left = rcItem.left;
rcLeftBottom.right = rcLeftBottom.left+m_rcBorder9grid.left;
rcLeftBottom.bottom = rcItem.bottom;
rcLeftBottom.top = rcLeftBottom.bottom-m_rcBorder9grid.bottom;
::TransparentBlt(hSrcDC, rcLeftBottom.left, rcLeftBottom.top, rcLeftBottom.Width(), rcLeftBottom.Height(), memdc, 0, m_nHeight-m_rcBorder9grid.bottom, m_rcBorder9grid.left, m_rcBorder9grid.bottom,RGB(255,0,255));
//左边
CRect rcLeft;
rcLeft.left = rcItem.left;
rcLeft.right = rcLeft.left+m_rcBorder9grid.left;
rcLeft.top = rcItem.top+m_rcBorder9grid.top;
rcLeft.bottom = rcItem.bottom-m_rcBorder9grid.bottom;
::StretchBlt(hSrcDC, rcLeft.left, rcLeft.top, rcLeft.Width(), rcLeft.Height(), memdc, 0, m_rcBorder9grid.top, m_rcBorder9grid.left, m_nHeight-m_rcBorder9grid.top-m_rcBorder9grid.bottom, SRCCOPY);
//顶边
CRect rcTop;
rcTop.left = rcItem.left+m_rcBorder9grid.left;
rcTop.right = rcItem.right-m_rcBorder9grid.right;
rcTop.top = rcItem.top;
rcTop.bottom = rcTop.top+m_rcBorder9grid.top;
::StretchBlt(hSrcDC, rcTop.left, rcTop.top, rcTop.Width(), rcTop.Height(), memdc, m_rcBorder9grid.left, 0, m_nWidth-m_rcBorder9grid.left-m_rcBorder9grid.right, m_rcBorder9grid.top, SRCCOPY);
//右边
CRect rcRight;
rcRight.right = rcItem.right;
rcRight.left = rcRight.right-m_rcBorder9grid.right;
rcRight.top = rcItem.top+m_rcBorder9grid.top;
rcRight.bottom = rcItem.bottom-m_rcBorder9grid.bottom;
::StretchBlt(hSrcDC, rcRight.left, rcRight.top, rcRight.Width(), rcRight.Height(), memdc, m_nWidth-m_rcBorder9grid.right, m_rcBorder9grid.top, m_rcBorder9grid.right, m_nHeight-m_rcBorder9grid.top-m_rcBorder9grid.bottom, SRCCOPY);
//底边
CRect rcBottom;
rcBottom.left = rcItem.left+m_rcBorder9grid.left;
rcBottom.right = rcItem.right-m_rcBorder9grid.right;
rcBottom.bottom = rcItem.bottom;
rcBottom.top = rcBottom.bottom-m_rcBorder9grid.bottom;
::StretchBlt(hSrcDC, rcBottom.left, rcBottom.top, rcBottom.Width(), rcBottom.Height(), memdc, m_rcBorder9grid.left, m_nHeight-m_rcBorder9grid.bottom, m_nWidth-m_rcBorder9grid.left-m_rcBorder9grid.right, m_rcBorder9grid.bottom, SRCCOPY);
//中心
CRect rcCenter;
rcCenter.left = rcItem.left+m_rcBorder9grid.left;
rcCenter.right = rcItem.right-m_rcBorder9grid.right;
rcCenter.top = rcItem.top+m_rcBorder9grid.top;
rcCenter.bottom = rcItem.bottom-m_rcBorder9grid.bottom;
int nMode = ::SetStretchBltMode(hSrcDC,HALFTONE);
::StretchBlt(hSrcDC, rcCenter.left, rcCenter.top, rcCenter.Width(), rcCenter.Height(), memdc, m_rcBorder9grid.left, m_rcBorder9grid.top, m_nWidth-m_rcBorder9grid.left-m_rcBorder9grid.right, m_nHeight-m_rcBorder9grid.top-m_rcBorder9grid.bottom, SRCCOPY);
::SetStretchBltMode(hSrcDC,nMode);
::DeleteDC(memdc);
}
return TRUE;}
为了更清楚,现把顶边代码去掉看看:
通过控件自绘,你能够按照自己的需要和想法来实现一些酷炫的界面外观。人靠衣装,佛靠金装,一个好的软件产品也需要精致的外表,至少在审美上要满足用户的需求,提高用户体验。
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言VC/MFC频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号