自绘按钮

来源:互联网 发布:食品经营许可网络平台 编辑:程序博客网 时间:2024/05/16 05:56
━━━━━━━━━━━━━━━━━━━━━━━━
    突然心血来潮,想写一个自绘按钮的类。以前做这事会觉得很辛苦,但现在随着自己知识和经验的增长,一切都变得简单很多了。刚刚做好,现与诸位分享。若有不正确之处,还望各位海涵并给于斧正。阿弥陀佛!^-^
━━━━━━━━━━━━━━━━━━━━━━━━

一、如何使用:
    1.把MyButton.cpp 和 MyButton.h 添加到你的工程里。
    2.包含头文件 #include "MyButton.h"
    3.在OnInitDialog()中添加以代码。(一切搞掂,是不是很方便呢)
    BOOL CDemoDlg::OnInitDialog()
    {
        //子类化单个按钮。
        //button close 时在 PostNcDestroy() 会调用delete this清除对象
        CMyButton*p1 =new CMyButton(m_hWnd,IDC_BUTTON1);
        CMyButton*p2 =new CMyButton(m_hWnd,IDC_BUTTON2);

    return TRUE;
    }

    4.需要子类化所有button时,按如下调用:
    BOOL CDemoDlg::OnInitDialog()
    {
        //子类化所有按钮
        CMyButton::SubclassAllButton(m_hWnd);

    return TRUE;
    }

━━━━━━━━━━━━━━━━━━━━━━━━
二、几个重点:
    1.要设置button为自绘。如下代码“ModifyStyle(0, BS_OWNERDRAW);”
    2、要手动添加鼠标离开窗口的消息(若不懂,请看捕获鼠标离开窗口的消息)
        http://hi.baidu.com/qiujiejia/blog/item/f22d012488c7880f4c088def.html
    3.成员函数DrawGradientV是画一个颜色渐变的矩形,你可以把它直接移植出来应用在你的工程里
    4.DrawMyItem函数分成三个步骤。
    (1)画渐变背景
    (2)画空的矩形框作为按钮的边框
    (3)画文字(如果鼠标按下,则偏移文字)

━━━━━━━━━━━━━━━━━━━━━━━━

三、把自绘按钮封装成一个dll

你只需要把ButtonSkin.dll放在工程目录下,并且在对话框初始化时添加以下代码即可使所有button都换上皮肤,很方便。
  
//加载按钮皮肤
    HINSTANCE hInst1;
    if (hInst1=LoadLibrary("ButtonSkin.dll"))
    {
        void (*SubclassButton)(HWND)=(void(*)(HWND))GetProcAddress(hInst1,"SubclassButton");
        if(SubclassButton)     SubclassButton(m_hWnd);
    }

ButtonSkin.dll 下载:ButtonSkin.dll
详细代码请参考源程序:ButtonSkin source code

━━━━━━━━━━━━━━━━━━━━━━━━

四、部分重要代码

CMyButton::CMyButton(HWND ParentWnd,UINT nID)
{
   SubclassWindow(::GetDlgItem(ParentWnd,nID)); //子类化
}

void CMyButton::PreSubclassWindow()
{
   m_IsInWindow=false;
   ModifyStyle(0, BS_OWNERDRAW);               //修改为自绘button
   ::GetClientRect(m_hWnd,&rect);               //获取button的矩形区域
   CButton::PreSubclassWindow();
}

/****************************************************************************
//子类化所有按钮
****************************************************************************/
void CMyButton::SubclassAllButton(HWND ParentWnd)
{
   HWND hChild = ::GetWindow(ParentWnd, GW_CHILD);
   while(hChild != NULL)
   {
        TCHAR lpClassName[MAX_PATH];
        ::GetClassName(hChild,lpClassName,MAX_PATH); //获得窗口类名
        if (_tcscmp(lpClassName,_T("Button"))==0)
        {
            // 判断按钮类型, 确保不是单选或复选按钮
            DWORD dwStyle = GetWindowLong(hChild, GWL_STYLE);
            dwStyle = dwStyle & 0x0000000F;
            if(dwStyle == BS_PUSHBUTTON || dwStyle == BS_DEFPUSHBUTTON)
            {
                //button close 时在 PostNcDestroy() 会调用delete this清除对象
                CMyButton* p=new CMyButton();
                p->SubclassWindow(hChild); //子类化
            }
        }
        hChild=::GetWindow(hChild, GW_HWNDNEXT);
   }
}

/****************************************************************************
在鼠标第一次进入窗口时,绘制按钮的over效果
****************************************************************************/
void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{
   if (!m_IsInWindow)
   {
        m_IsInWindow = TRUE;
        TRACKMOUSEEVENT trackmouseevent;
        trackmouseevent.cbSize = sizeof(trackmouseevent);
        trackmouseevent.dwFlags = TME_LEAVE;
        trackmouseevent.hwndTrack = GetSafeHwnd();
        trackmouseevent.dwHoverTime = HOVER_DEFAULT;
        _TrackMouseEvent(&trackmouseevent);  

        //draw move over effect
        DrawMyItem(RGB(0,225,225),RGB(175,238,238));
   }  
   CButton::OnMouseMove(nFlags, point);
}

/****************************************************************************
鼠标离开按钮,置m_IsInWindow为false,并刷新按钮
****************************************************************************/
LRESULT CMyButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{  
   m_IsInWindow=false;      
   InvalidateRect(NULL);
   return 0;
}  

/****************************************************************************
核心代码。
COLORREF co1, COLORREF co2 组合成渐变色
****************************************************************************/
void CMyButton::DrawMyItem(COLORREF co1, COLORREF co2)
{
   HDC hdc=::GetDC(m_hWnd);

   //draw gradient effect
   DrawGradientV(hdc,co1,co2,rect);

   //draw a null rectangle as the button frame
   HBRUSH OldBrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)GetStockObject(NULL_BRUSH));
   ::Rectangle(hdc,0,0,rect.right-rect.left,rect.bottom-rect.top);
   ::SelectObject (hdc,OldBrush) ;

   //draw button text
   TCHAR WindowText[100];
   ::GetWindowText(m_hWnd, WindowText, MAX_PATH);

   //设置文字透明
   int nMode=::SetBkMode(hdc,TRANSPARENT);  

   //鼠标按下时字体偏移
   if (GetKeyState(VK_LBUTTON)<0)
   {
        CRect TempRect=rect;
        TempRect.OffsetRect(1,1);
        ::DrawText (hdc,WindowText, -1,&TempRect,DT_SINGLELINE | DT_CENTER | DT_VCENTER |DT_VCENTER|DT_END_ELLIPSIS ) ;
   }  
   else
        ::DrawText (hdc,WindowText, -1,&rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER |DT_VCENTER|DT_END_ELLIPSIS ) ;

   ::SetBkMode(hdc,nMode);

   ::ReleaseDC(m_hWnd,hdc);
}



/****************************************************************************
收到消息要求我们来绘制button
****************************************************************************/
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
   if (lpDrawItemStruct->itemState & ODS_FOCUS )
   {
        if (lpDrawItemStruct->itemState & ODS_SELECTED ) //鼠标按下并且在窗口外
            DrawMyItem(RGB(0,255,0),RGB(0,255,200));
        else
            DrawMyItem(RGB(49,68,212),RGB(147,255,255)); //获得焦点的效果
   }
   else
        DrawMyItem(RGB(253,181,81),RGB(253,255,206)); //没有获得焦点的效果
}




/****************************************************************************
绘制垂直颜色渐变区域
DrawGradientV(   HDC hdc           //绘图刷子
                COLORREF co1   //顶端颜色
                COLORREF co2   //低端颜色
                RECT& DrawRect)   //颜色渐变区域
****************************************************************************/

//设置渐变参数,GRADLEVEL越小,颜色渐变越细腻,过度效果越好,但速度比较慢
#define GRADLEVEL 1  

void CMyButton::DrawGradientV( HDC hdc, COLORREF co1, COLORREF co2, RECT& DrawRect )
{
   int r = GetRValue( co1 );
   int g = GetGValue( co1 );
   int b = GetBValue( co1 );
  
   int r2 = GetRValue( co2 );
   int g2 = GetGValue( co2 );
   int b2 = GetBValue( co2 );

   //计算宽,高
   int DrawRectWidth=DrawRect.right-DrawRect.left;
   int DrawRectHeight=DrawRect.bottom-DrawRect.top;

   if ( DrawRectWidth<=0)
        return;

   //初始化rect
   RECT rect={0,0,DrawRectWidth,GRADLEVEL};
  
   //准备GDI
   HDC hMemDC=CreateCompatibleDC(hdc);               //创建内存DC
   HBITMAP hBitmap=::CreateCompatibleBitmap(hdc,DrawRectWidth,DrawRectHeight);//创建位图
   ::SelectObject(hMemDC,hBitmap);       //把位图选进内存DC
   HBRUSH hbr;


   for(int i = DrawRectHeight; i > 0; i -= GRADLEVEL )
   {
        //创建刷子
        hbr = CreateSolidBrush( RGB( r, g, b ) );
        FillRect( hMemDC, &rect, hbr );
        DeleteObject( hbr );

        //改变小正方体的位置
        rect.top += GRADLEVEL;
        rect.bottom += GRADLEVEL;

        //判断小正方体是否超界
        if( rect.bottom > DrawRect.bottom )
            rect.bottom = DrawRect.bottom;
       
        //改变颜色
        r += ( r2 - r + i / 2 ) / i * GRADLEVEL;
        g += ( g2 - g + i / 2 ) / i * GRADLEVEL;
        b += ( b2 - b + i / 2 ) / i * GRADLEVEL;
   }

   //内存DC映射到屏幕DC
   BitBlt(hdc,DrawRect.left,DrawRect.top,DrawRectWidth,DrawRectHeight,hMemDC,0,0,SRCCOPY);  

   //删除
   ::DeleteDC(hMemDC) ;                      
   ::DeleteObject(hBitmap);  
}




void CMyButton::PostNcDestroy()
{
   delete this;  
   CButton::PostNcDestroy();
}

━━━━━━━━━━━━━━━━━━━━━━━━
原创粉丝点击