VC编程按钮自绘的简单实现
小标 2018-08-14 来源 : 阅读 966 评论 0

摘要:本文主要向大家介绍了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频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程