C++/MFC——一个能作出PhotoShop工具栏的Button控件

原创 2007年09月13日 10:32:00

 这个控件能够制作出和PhotoShop几乎一样的工具栏。当然换了图片以后也能作出其他的效果。能够完美的支持三态(Normal,Mouse Over,Checked)。
描画部分使用了GDI+,如果没有安装的话,安装一下就好了。GDI+是一个微软免费提供的描画库,是WIN平台上GDI库的改进版,拥有很多方便的功能。下载后,解压缩到一个位置,然后在VC6的Tools / Options / Directories设置一下就可以了(其他IDE大致上都差不多)。

.h文件

 /*************************************************************************
 * Copyright (c) 2007 李兰非(Li Lanfei)
 * Ver: 1.0
 * Date : 2004.12.15
 * Email :  TheSmallCar@Gmail.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to 
 * deal in the Software without restriction, including without limitation the 
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 
 * sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
 * THE SOFTWARE.
 ************************************************************************
*/



#if !defined(AFX_BMPBUTTON_H__5D262D01_6F1C_43DF_B7EC_5CD86FFB1A88__INCLUDED_)
#define AFX_BMPBUTTON_H__5D262D01_6F1C_43DF_B7EC_5CD86FFB1A88__INCLUDED_

//包含了std::string,并使用了std的名字空间
#include <String>
using namespace std;

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// BMPButton.h : header file


//////////////////////////////[ For Init GDI+ ]///////////////////////////
// 该部分加载了GDI+ 的相关头文件和lib文件
// 需要GDI+的支持,相关的文件路径也需要设置正确。
// 建议在 [ Tools / Options / Directories ]里设置正确的路径
#ifndef _GDIPLUS_H
#define _SELF_IMPORT_GDI_PLUS
#include 
<comdef.h>

#if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
#else
typedef unsigned 
long ULONG_PTR;
#endif

#include 
"GdiPlus.h"

#pragma comment( lib, "gdiplus.lib" )

using namespace Gdiplus;
#endif

//////////////////////////////[ For Init GDI+ End ]////////////////////////


//按钮状态类型
enum ButtonStatus
{
    B_NORMAL,        
//正常
    B_DOWN,            //按下
    B_CHECK,        //选中(BT_CHECK)风格有效
    B_MOUSE_OVER,    //鼠标悬浮
    B_DISABLE        //无效
}
;

//按钮风格类型
enum ButtonType
{
    BT_NORMAL,        
//正常
    BT_CHECK        //Check Box,即三态按钮
}
;

/////////////////////////////////////////////////////////////////////////////
// BMPButton window

class CBMPButton : public CButton
{
// Construction
public:
    CBMPButton();

// Attributes
public:

// Operations
public:

// Overrides
    
// ClassWizard generated virtual function overrides
    
//{{AFX_VIRTUAL(CBMPButton)
    public:
    
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    
protected:
    
virtual void PreSubclassWindow();
    
//}}AFX_VIRTUAL

// Implementation
public:
    
virtual ~CBMPButton();

    
// Generated message map functions
protected:
    
//{{AFX_MSG(CBMPButton)
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg LRESULT OnMouseLeave( WPARAM wparam, LPARAM lparam );
    afx_msg 
void OnPaint();
    afx_msg 
void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg 
void OnLButtonUp(UINT nFlags, CPoint point);
    afx_msg 
void OnTimer(UINT nIDEvent);
    
//}}AFX_MSG

    DECLARE_MESSAGE_MAP()


// For GDI+
#if _SELF_IMPORT_GDI_PLUS
private:
    GdiplusStartupInput m_gdiplusStartupInput;
    ULONG_PTR m_pGdiToken;
#endif

public:

    
void SetImage( CString Normal, CString Hot = "", CString Click = "", CString Disable = "" );

    
void SetPosition( int l, int t, int w, int h );

    ButtonStatus SetCheck( BOOL    Check );

    BOOL GetCheck();

    
void SetType( ButtonType Type );
    
    ButtonType GetType();

    
void SetMenu( UINT MenuID );

    BOOL EnableWindow( BOOL bEnable );

private:
    
    BOOL DrawImage( wstring File, Color BGColor 
= Color::Black );

    BOOL DrawHot();

    BOOL DrawClick();

    BOOL DrawDisable();

    BOOL DrawRectangle( Pen 
&pen, wstring ImageFile = L"", Color BGColor = Color::Black );
    
private:
    
    
// 状态图片路径
    wstring m_Normal;
    wstring m_Hot;
    wstring m_Click;
    wstring m_Disable;

    
//按钮位置
    CRect    m_Rect;

    
//当前活动图片
    wstring m_ActiveImage;

    
//状态 & 风格
    ButtonStatus m_Statu;
    ButtonType   m_Type;

    
//绑定菜单
    UINT    m_MenuID;

    
//菜单弹出控制定时器
    UINT    m_LBDownTimer;
}
;

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BMPBUTTON_H__5D262D01_6F1C_43DF_B7EC_5CD86FFB1A88__INCLUDED_)

.cpp文件

// BMPButton.cpp : implementation file
//

#include 
"stdafx.h"
#include 
"..easydraw.h"
#include 
"BMPButton.h"
#include 
"MenuEx.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


GraphicsPath
& MakeRoundRect(Point topLeft, Point bottomRight, INT percentageRounded)
{
    ASSERT ( percentageRounded 
>= 0 && percentageRounded <= 100 );

    INT left  
= min(topLeft.X, bottomRight.X);
    INT right 
= max(topLeft.X, bottomRight.X);
    INT top    
= min(topLeft.Y, bottomRight.Y);
    INT bottom 
= max(topLeft.Y, bottomRight.Y);
    
    INT offsetX 
= (right-left)*percentageRounded/100;  
    INT offsetY 
= (bottom-top)*percentageRounded/100;

    
static GraphicsPath path;

    path.Reset();

    path.AddArc( right
-offsetX, top, offsetX, offsetY, 27090 );
    path.AddArc( right
-offsetX, bottom-offsetY, offsetX, offsetY, 090 );
    path.AddArc( left, bottom 
- offsetY, offsetX, offsetY, 9090 );
    path.AddArc( left, top, offsetX, offsetY, 
18090 );
    
    path.AddLine( left 
+ offsetX, top, right - offsetX/2, top );

    
return path;
}


/////////////////////////////////////////////////////////////////////////////
// BMPButton


CBMPButton::CBMPButton()
{
#if _SELF_IMPORT_GDI_PLUS
    GdiplusStartup( 
&m_pGdiToken , &m_gdiplusStartupInput , NULL );
#endif

    m_Statu 
= B_NORMAL;
    m_Type 
= BT_NORMAL;
    m_MenuID 
= 0;
    
    m_LBDownTimer 
= 0;
}


CBMPButton::
~CBMPButton()
{
#if _SELF_IMPORT_GDI_PLUS
    GdiplusShutdown(m_pGdiToken);
#endif
}



BEGIN_MESSAGE_MAP(CBMPButton, CButton)
    
//{{AFX_MSG_MAP(CBMPButton)
    ON_WM_MOUSEMOVE()
    ON_MESSAGE( WM_MOUSELEAVE, OnMouseLeave )
    ON_WM_PAINT()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
    ON_WM_TIMER()
    
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// BMPButton message handlers

void CBMPButton::OnMouseMove(UINT nFlags, CPoint point) 
{
    
if( m_Statu != B_DOWN && m_Statu != B_CHECK )
    
{
        DrawHot();
        
        m_Statu 
= B_MOUSE_OVER;
    }
    
    
    TRACKMOUSEEVENT   tme;  
    tme.cbSize   
=   sizeof(tme);  
    tme.hwndTrack   
=   m_hWnd;  
    tme.dwFlags   
=   TME_LEAVE;
    _TrackMouseEvent(
&tme); 
    
    CButton::OnMouseMove(nFlags, point);
}


LRESULT CBMPButton::OnMouseLeave( WPARAM wparam, LPARAM lparam )
{
    
if( m_Statu != B_CHECK )
    
{
        m_Statu 
= B_NORMAL;
        
if( DrawImage(m_Normal) )    return S_OK;
        
else                        return E_FAIL;
    }

    
else
    
{
        
if!DrawImage(m_Click) )
        
{
            
if( DrawClick() )    return S_OK;
            
else    return E_FAIL;
        }

    }


    Invalidate();
    
return S_OK;
}


void CBMPButton::SetImage( CString Normal, CString Hot, CString Click, CString Disable )
{
    m_Normal 
= Normal.AllocSysString();
    m_Hot 
= Hot.AllocSysString();
    m_Click 
= Click.AllocSysString();
    m_Disable 
= Disable.AllocSysString();

    
if( m_Rect.Width() * m_Rect.Height() == 0 )
        GetWindowRect(m_Rect);

    DrawImage( m_Normal );
}


void CBMPButton::SetPosition( int l, int t, int w, int h )
{
    m_Rect 
= CRect( l, t, l+w, t+h );

    MoveWindow( m_Rect );
}


BOOL CBMPButton::DrawImage( wstring File, Color BGColor )
{
    
if( File.size() == 0 )    return FALSE;
    
    CDC
* pDC = GetDC();

    
//获得系统默认的界面颜色
    Color Face;    
    COLORREF color 
= GetSysColor(COLOR_3DFACE);
    Face.SetFromCOLORREF(color);
    
    
if( BGColor.ToCOLORREF() == RGB(0,0,0) )
    
{
        BGColor 
= Face;
    }


    Graphics Graphic(
*pDC);
    Bitmap Bmp(File.c_str());
    
    
if( Bmp.GetLastStatus() != Ok )    return FALSE;

    
//刷新背景
    Graphic.Clear(Face);

    Rect rect( 
00, m_Rect.Width()-1, m_Rect.Height()-1 );    

    GraphicsPath 
&Path = MakeRoundRect( Point( 0,0 ), Point( rect.Width,rect.Height ), 25 );

    SolidBrush PathBrush( BGColor );

    Graphic.FillPath( 
&PathBrush, &Path );
    
    rect 
= Rect( 00, m_Rect.Width(), m_Rect.Height() );

    
//描画图片
    Graphic.DrawImage( &Bmp, rect );

    m_ActiveImage 
= File;

    
return TRUE;
}


void CBMPButton::OnPaint() 
{
    CPaintDC dc(
this); // device context for painting

    
if( B_CHECK == m_Statu || B_DOWN == m_Statu )
    
{
        DrawClick();
    }

    
else if( B_MOUSE_OVER == m_Statu )
    
{
        DrawHot();
    }

    
else if( B_DISABLE == m_Statu )
    
{
        DrawDisable();
    }

    
else            
        DrawImage( m_ActiveImage );
}


BOOL CBMPButton::DrawHot()
{
    
if( DrawImage(m_Hot) )    return TRUE;

    CDC
* pDC = GetDC();    
    Graphics Graphic(
*pDC);

    Pen pen( Color::Black, 
1 );
    pen.SetDashStyle( DashStyleCustom );
    REAL dashVals[
2= 11 };
    pen.SetDashPattern(dashVals, 
2);

    DrawRectangle( pen, m_Normal );
    
    
return TRUE;
}


BOOL CBMPButton::DrawClick()
{
    
if( DrawImage(m_Click) )    return TRUE;
    Pen pen( Color::Black, 
1 );
    pen.SetDashStyle(DashStyleSolid);

    DrawRectangle( pen, m_Normal, Color( 
255255255255 ) );

    
return TRUE;
}


BOOL CBMPButton::DrawDisable()
{
    
if( DrawImage(m_Disable) )    return TRUE;

    CDC
* pDC = GetDC();
    Graphics Graphic(
*pDC);

    Color Face;    
    COLORREF color 
= GetSysColor(COLOR_3DFACE);
    Face.SetFromCOLORREF(color);

    Graphic.Clear(Face);

    ColorMatrix colorMatrix 
= {
        
0.3f,    0.3f,    0.3f,    0.0f,    0.0f,
        
0.59f,    0.59f,    0.59f,    0.0f,    0.0f,
        
0.11f,    0.11f,    0.11f,    0.0f,    0.0f,
        
0.0f,    0.0f,    0.0f,    0.3f,    0.0f,
        
0.0f,    0.0f,    0.0f,    0.0f,    1.0f }
;

    ImageAttributes imageAtt;
    imageAtt.SetColorMatrix( 
&colorMatrix );

    Bitmap Bmp( m_Normal.c_str() );
    INT iWidth 
= Bmp.GetWidth();
    INT iHeight 
= Bmp.GetHeight();

    Graphic.DrawImage(
        
&Bmp, 
        Rect(
00, m_Rect.Width(), m_Rect.Height() ),    // Destination rectangle
        0,                                                // Source rectangle X 
        0,                                                // Source rectangle Y
        iWidth,                                    // Source rectangle width
        iHeight,                                // Source rectangle height
        UnitPixel, 
        
&imageAtt);

    
return TRUE;
}


BOOL CBMPButton::DrawRectangle( Pen 
&pen, wstring ImageFile, Color BGColor )
{
    CDC
* pDC = GetDC();

    Graphics Graphic(
*pDC);

    Rect rect( 
00, m_Rect.Width()-1, m_Rect.Height()-1 );

    
if( ImageFile.size() )
        
if( FALSE == DrawImage( ImageFile, BGColor ) )
            
return FALSE;
    

    GraphicsPath 
&Path = MakeRoundRect( Point( 0,0 ), Point( rect.Width,rect.Height ), 25 );

    Graphic.DrawPath( 
&pen, &Path );

    
return TRUE;
}


ButtonStatus CBMPButton::SetCheck( BOOL    Check )
{
    
if( m_Type == BT_NORMAL )    return m_Statu;

    ButtonStatus l 
= m_Statu;
    
    
if( Check )
        m_Statu 
= B_CHECK;
    
else
        m_Statu 
= B_DOWN;

    Invalidate();

    
return l;
}


BOOL CBMPButton::GetCheck()
{
    
if( m_Statu == B_CHECK )    return TRUE;
    
else                        return FALSE;
}


void CBMPButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
    
if( B_DOWN == m_Statu )
        DrawClick();
}


void CBMPButton::PreSubclassWindow() 
{
    UINT nBS;

    nBS 
= GetButtonStyle();

    
// Switch to owner-draw
    ModifyStyle(SS_TYPEMASK, BS_OWNERDRAW, SWP_FRAMECHANGED);

    CButton::PreSubclassWindow();
}


void CBMPButton::OnLButtonDown(UINT nFlags, CPoint point) 
{
    CRect rect;
    GetClientRect(rect);
    
    
if( rect.PtInRect(point) )
    
{
        
if( m_Statu != B_CHECK )
            m_Statu 
= B_DOWN;
    }

    
else
        
return;

    DrawClick();

    m_LBDownTimer 
= SetTimer( 0x00ff00ff300, NULL );

    Invalidate();

    CButton::OnLButtonDown(nFlags, point);
}


void CBMPButton::OnLButtonUp(UINT nFlags, CPoint point) 
{
    KillTimer(m_LBDownTimer);
    m_LBDownTimer 
= 0;

    
if( m_Type != BT_NORMAL && m_Statu != B_CHECK )
    
{
        m_Statu 
= B_CHECK;
        DrawClick();
    }

    
else
    
{
        m_Statu 
= B_MOUSE_OVER;
        CRect rect;
        GetClientRect(rect);
        
        
if( rect.PtInRect(point) )
        
{
            DrawHot();
        }

        
else
            DrawImage(m_Normal);
    }


    Invalidate();
    
    CButton::OnLButtonUp(nFlags, point);
}


void CBMPButton::SetType( ButtonType Type )
{
    m_Type 
= Type;
}


ButtonType CBMPButton::GetType()
{
    
return m_Type;
}


void CBMPButton::SetMenu( UINT MenuID )
{
    m_MenuID 
= MenuID;
    
    CMenu Menu;
    
    
if( FALSE == Menu.LoadMenu( m_MenuID ) )
    
{
        CString Msg;
        Msg.Format(
"菜单资源#%d载入失败!", m_MenuID );
        AfxMessageBox(Msg);
    }

}


void CBMPButton::OnTimer(UINT nIDEvent) 
{
    
if( nIDEvent == m_LBDownTimer )
    
{
        KillTimer(m_LBDownTimer);
        m_LBDownTimer 
= 0;

        CMenu menu;    
        
if( m_MenuID && menu.LoadMenu( m_MenuID ) )
        
{
            CMenu
* pSubMenu = menu.GetSubMenu(0);

//            CPoint Mouse;
//            GetCursorPos(&Mouse);
//            pSubMenu->TrackPopupMenu(TPM_LEFTBUTTON, Mouse.x, Mouse.y, GetParent(), m_Rect );

            CRect WndRect;
            GetWindowRect( WndRect );
            pSubMenu
->TrackPopupMenu(TPM_LEFTBUTTON, WndRect.left + m_Rect.Width(), WndRect.top + m_Rect.Height() - 15, GetParent() );
        }

    }

    
    CButton::OnTimer(nIDEvent);
}


BOOL CBMPButton::EnableWindow( BOOL bEnable )
{
    
if( bEnable )
    
{
        m_Statu 
= B_NORMAL;
    }

    
else
    
{
        m_Statu 
= B_DISABLE;
        
if!DrawImage(m_Disable) )
            DrawDisable();
    }


    
return CWnd::EnableWindow(bEnable);
}

图例

        

           正常                            鼠标悬浮                      按下/选中                           弹出菜单

通过上图可以看出,它的基本效果。如果懒得做四幅图片(还有一个无效的灰色的图片),只要做一个正常状态下的图片就可以了,上面的例子就是。其他的鼠标悬浮,按下和无效几个状态都可以自动生成,方便吧!

具体的使用示例如下

//在头文件添加的成员变量
CBMPButton    m_Curve;
CBMPButton    m_Line;



//.cpp部分,直接加到OnInitDialog()或其他地方即可
m_Line.SetImage( "F:/SelfCode/EasyDraw/res/Line.ico" );    //只设置了一个Normal的图片    
m_Line.SetPosition( 10102424 );                                                  //设置位置和大小
m_Line.SetType( BT_CHECK );                                                          //设置类型,有Normal和Check两种
m_Line.SetMenu( SUB_MENU_PEN_LINE );                                    //添加绑定的菜单
m_Line.EnableWindow(TRUE);                                                          //设置可操作性

m_Curve.SetImage( 
"F:/SelfCode/EasyDraw/res/Curve.ico" );    
m_Curve.SetPosition( 
39102424 );
m_Curve.SetType( BT_CHECK );
m_Curve.EnableWindow(TRUE); 

相关文章推荐

C++MFC学习心得(四)——CListBox自绘控件碰到的一个小问题

老规矩,先贴代码 //.hclass CMyApp:public CWinApp { public: virtual BOOL InitInstance(); }; class CIconList...

MFC基本控件使用——按钮(Button)

昨天写了一个常用的控件Staticji

MFC工具栏(CToolBar)控件常见操作

工具栏 工具栏控件在控件面板里没有对应的选项(图标),但有一个工具栏控件类CToolBar,所以我们如果要创建一个工具栏控件并显示在窗口里的话,只能用代码来完成,事实上任何一种控件,都可以用代码创建...

VC MFC工具栏(CToolBar)控件以及如何设置按钮图片集

2011-05-24 14:00 VC MFC工具栏(CToolBar)控件 工具栏 工具栏控件在控件面板里没有对应的选项(图标),但有一个工具栏控件类CToolBar,所以我们如果要创建一个工...

【转】VC MFC工具栏(CToolBar)控件

from:http://hi.baidu.com/rainminism/item/f2ad45d45883af2c39f6f71c 工具栏 工具栏控件在控件面板里没有对应的选项(图标),但有一个工...
  • mail_cm
  • mail_cm
  • 2012年09月21日 16:21
  • 502

MFC工具栏(CToolBar)控件

工具栏 工具栏控件在控件面板里没有对应的选项(图标),但有一个工具栏控件类CToolBar,所以我们如果要创建一个工具栏控件并显示在窗口里的话,只能用代码来完成,事实上任何一种控件,都可以用...

VC MFC工具栏(CToolBar)控件

工具栏 工具栏控件在控件面板里没有对应的选项(图标),但有一个工具栏控件类CToolBar,所以我们如果要创建一个工具栏控件并显示在窗口里的话,只能用代码来完成,事实上任何一种控件,都可以用代码创建...

【VC+MFC】在工具栏上添加ComboBox控件的方法

最近在做一个软件开发,需要在工具栏上添加
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++/MFC——一个能作出PhotoShop工具栏的Button控件
举报原因:
原因补充:

(最多只允许输入30个字)