天天看点

WTL 自绘控件库 (CQsRadioBox)

概述:

代码实现如下:

#pragma once
#include "QsInclude.h"

#define  MAXRADIOXTXT       512

#define     QS_RAD_SYSYSTEM             0x00000001          //RadioBox系统样式
#define     QS_RAD_OWNERDRAW        0x00000002          //RadioBox自绘样式,默认为自绘样式。

class CQsRadioBox :
    public CWindowImpl<CQsRadioBox, CButton>,
    public CImageMgrCtrlBase< CQsRadioBox>,
    public COwnerDraw< CQsRadioBox >
{
    typedef CWindowImpl< CQsRadioBox, CButton > theBaseClass;
    typedef CImageMgrCtrlBase< CQsRadioBox > theImageCtrlBaseClass;

    Image *m_pCurImage;                         //当前正在使用的图片
    Image *m_pLastImage;                            //发生状态改变前使用的图片
    volatile int m_nCheck;                          //是否被选中标志
    volatile bool m_bMouseDown;             //鼠标左键按下
    Color m_enableColor;                       //可用状态时字体的颜色
    Color m_unenableColor;                  //不可用状态时字体的颜色
public:
    BEGIN_MSG_MAP( CQsRadioBox )
        MESSAGE_HANDLER( WM_ERASEBKGND, OnEraseBKGnd )
        MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
        MESSAGE_HANDLER( WM_LBUTTONUP, OnLButtonUp )
        MESSAGE_HANDLER( WM_MOUSELEAVE, OnMouseLeave )
        MESSAGE_HANDLER( WM_MOUSEMOVE, OnMouseMove )
        MESSAGE_HANDLER( BM_SETCHECK, OnSetCheck )
        MESSAGE_HANDLER( BM_GETCHECK, OnGetCheck )
        REFLECTED_COMMAND_CODE_HANDLER( BN_CLICKED, OnClicked )
        CHAIN_MSG_MAP_ALT( COwnerDraw< CQsRadioBox >, 1 )
        CHAIN_MSG_MAP( theImageCtrlBaseClass )
        DEFAULT_REFLECTION_HANDLER()
    END_MSG_MAP()

    /**
    *@method   CQsRadioBox
    *@brief    CQsRadioBox类构造函数
    *    
    *@return   
    */
    CQsRadioBox():
        m_bMouseDown( false ),
        m_pCurImage( NULL ),
        m_pLastImage( NULL ),
        m_enableColor(255, 0, 0, 0),
        m_unenableColor(255, 128, 128, 128),
        m_nCheck( 0 )
    {
        m_uFirstPos = CONTROL_RDB_FIRST;
        m_uLastPos = CONTROL_RDB_LAST;
        SetQsStyle(QS_RAD_OWNERDRAW);
    }

    /**
    *@method   ~CQsRadioBox
    *@brief    CQsRadioBox析构造函数
    *    
    *@return   
    */
    virtual ~CQsRadioBox()
    {
    }

    /**
    *@method   DrawItem
    *@brief    按钮重画函数
    *    
    *@param    LPDRAWITEMSTRUCT lpdis 详见MSN
    *@return   void
    */
    void DrawItem( LPDRAWITEMSTRUCT lpdis )
    {
        int width = lpdis->rcItem.right - lpdis->rcItem.left;
        int height = lpdis->rcItem.bottom - lpdis->rcItem.top;

        //创建内存作图对象
        WTL::CDC memDC;
        memDC.CreateCompatibleDC( lpdis->hDC );
        WTL::CBitmap memBitmap;
        memBitmap.CreateCompatibleBitmap( lpdis->hDC, width, height );
        HBITMAP hOldBmp = memDC.SelectBitmap( memBitmap );

        //获得控件背景
        memDC.SetBkMode( TRANSPARENT );
        ::SendMessage( GetParent(), WM_DRAWBKGNDUI, ( WPARAM )memDC.m_hDC, ( LPARAM )lpdis->hwndItem );

        //绘制按钮
        DrawButton( memDC.m_hDC, lpdis->rcItem );

        //提交图像
        ::BitBlt( lpdis->hDC, 0, 0, width, height, memDC.m_hDC, 0, 0, SRCCOPY );

        memDC.SelectBitmap( hOldBmp );
        memBitmap.DeleteObject();
        memDC.DeleteDC();

    }

    /**
    *@method   Create
    *@brief    通过Create来创建窗口。
    *    
    *@param    HWND hWndParent  parent window
    *@param    ATL::_U_RECT rect = NULL create window rect
    *@param    LPCTSTR szWindowName = NULL the window name
    *@param    DWORD dwStyle = 0  base window style
    *@param    DWORD dwExStyle = 0  extended window style
    *@param    ATL::_U_MENUorID MenuOrID = 0U menu or ID
    *@param    LPVOID lpCreateParam = NULL create param
    *@return   HWND 非NULL success return TRUE, failed return NULL
    */
    HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
        DWORD dwStyle = 0, DWORD dwExStyle = 0,
        ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
    {
        return theBaseClass::Create( hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);
    }

    /**
    *@method   SubclassWindow
    *@brief    类对象关联
    *    
    *@param    HWND hWnd  对象句柄
    *@return   BOOL
    */
    BOOL SubclassWindow( HWND hWnd )
    {
        //在此之前可能已经SetCheck
        m_nCheck = (int)::SendMessage( hWnd, BM_GETCHECK, 0, 0 );

        BOOL bRet = theBaseClass::SubclassWindow( hWnd );
        if(m_dwQsStyle&QS_RAD_OWNERDRAW)
        {
            UINT nBS = GetButtonStyle();
            SetButtonStyle( nBS | BS_OWNERDRAW );
        }

        return bRet;
    }

    /**
    *@method   SetEnableColor
    *@brief    设置可用时字体的颜色。
    *    
    *@param    Color enableColor 可用时字体的颜色
    *@return   void
    */
    void SetEnableColor(Color enableColor)
    {
        m_enableColor =  enableColor;
    }

    /**
    *@method   SetUnEnableColor
    *@brief    设置不可用时字体的颜色
    *    
    *@param    Color unenableColor 不可用时字体的颜色
    *@return   void
    */
    void SetUnEnableColor(Color unenableColor)
    {
        m_unenableColor = unenableColor;
    }
protected:

    /**
    *@method   OnSetCheck
    *@brief    设置状态消息响应函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled     未被使用
    *@return   LRESULT
    */
    LRESULT OnSetCheck( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/ )
    {
        if(m_dwQsStyle&QS_RAD_SYSYSTEM)
        {
            return DefWindowProc(uMsg, wParam, lParam); 
        }
        //首先获得check标志
        m_nCheck = ( wParam != 0 );
        if( m_nCheck )
        {
            CSimpleArray< HWND > lst;
            lst.Add( m_hWnd );

            //获得父窗口句柄
            HWND hWndParent = GetParent().m_hWnd;

            //获得控件窗口句柄
            HWND hWnd = m_hWnd;
            while( hWnd )
            {
                //获得同组内控件的窗口句柄
                hWnd = ::GetNextDlgGroupItem( hWndParent, hWnd, 0 );

                //在list中查找这个句柄是否被查询到过
                int nPos = lst.Find( hWnd );
                if( nPos != -1 )
                {
                    //如果找到了这个句柄,那就代表搜索到头了
                    hWnd = NULL;
                }

                if( NULL != hWnd )
                {
                    //如果没有搜索完一组,则把当前搜索的句柄添加到list中去(目的是为了不让同一控件被发送两次消息)
                    lst.Add( hWnd );
                    //对这个控件发送unChecked消息
                    ::PostMessage( hWnd, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0 );
                }
            }
        }
        Invalidate();
        return 0;
    }

    /**
    *@method   OnGetCheck
    *@brief    读取状态消息响应函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled    未被使用
    *@return   LRESULT
    */
    LRESULT OnGetCheck( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/ )
    {
        if(m_dwQsStyle&QS_RAD_SYSYSTEM)
        {
            return DefWindowProc(uMsg, wParam, lParam); 
        }

        return m_nCheck;
    }

    /**
    *@method   OnLButtonDown
    *@brief     鼠标左键被按下消息响应函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled    未被使用
    *@return   LRESULT
    */
    LRESULT OnLButtonDown( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled )
    {
        m_bMouseDown = true;
        Invalidate();
        bHandled = FALSE;
        return 0;
    }

    /**
    *@method   OnMouseMove
    *@brief    鼠标进入消息响应函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled    未被使用
    *@return   LRESULT
    */
    LRESULT OnMouseMove( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled )
    {
        if( m_pCurImage != GetImage( CONTROL_BTN_MOUSEIN ) )
        {
            Invalidate();

            // 启动鼠标离开时间
            TRACKMOUSEEVENT tme;
            tme.cbSize  = sizeof(tme);
            tme.hwndTrack = m_hWnd;
            tme.dwFlags = TME_LEAVE;
            TrackMouseEvent(&tme);
        }

        bHandled = FALSE;
        return 0;
    }

    /**
    *@method   OnMouseLeave
    *@brief    鼠标离开消息响应函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled   未被使用
    *@return   LRESULT
    */
    LRESULT OnMouseLeave( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled )
    {
        Invalidate();
        bHandled = FALSE;
        return 0;
    }

    /**
    *@method   OnLButtonUp
    *@brief     鼠标左键被放开消息响应函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled   未被使用
    *@return   LRESULT
    */
    LRESULT OnLButtonUp( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled )
    {
        m_bMouseDown = false;
        Invalidate();
        bHandled = FALSE;
        return 0;
    }

    /**
    *@method   OnEraseBKGnd
    *@brief    背景绘制消息函数
    *    
    *@param    UINT uMsg     消息类型
    *@param    WPARAM wParam 未被使用
    *@param    LPARAM lParam 详见MSDN
    *@param    BOOL& bHandled  未被使用
    *@return   LRESULT
    */
    LRESULT OnEraseBKGnd( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
    {
        //禁止绘制底色
        return 0;
    }

    /**
    *@method   OnClicked
    *@brief     按钮被按下消息响应函数
    *    
    *@param    WORD wNotifyCode  通知消息代码
    *@param    WORD wID    发送该消息的控件ID
    *@param    HWND hWndCtl 发送该消息的控件的句柄
    *@param    BOOL& bHandled  消息是否继续处理标志
    *@return   LRESULT
    */
    LRESULT OnClicked(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
        SetCheck( BST_CHECKED );

        Invalidate();
        return 0;
    }


private:

    /**
    *@method   GetButtonTextFormat
    *@brief    得到Button文字的对齐方式(用DrawText()输出时的格式)
    *    
    *@param    const LONG lStyle 控件风格
    *@return   UINT  输出时的格式
    * 说  明: button上的字必须是一行
    */
    UINT GetButtonTextFormat(const LONG lStyle)
    {
        UINT uFormat = DT_SINGLELINE;//button上的字必须是一行
        
        //x方向
        if ( (lStyle & BS_CENTER)==BS_CENTER )//x方向,中
        {
            uFormat |= DT_CENTER;
        }
        else if ( (lStyle & BS_RIGHT)==BS_RIGHT )//x方向,右
        {
            uFormat |= DT_RIGHT;
        }
        else if ( (lStyle & BS_LEFT) == BS_LEFT )//x方向,左
        {
            uFormat |= DT_LEFT;
        }
        else//缺省,x中
        {
            uFormat |= DT_LEFT;
        }
        
        //y方向
        if ( (lStyle & BS_VCENTER ) == BS_VCENTER )//y,中
        {
            uFormat |= DT_VCENTER;
        }
        else if ( (lStyle & BS_TOP)==BS_TOP )//y方向,上
        {
            uFormat |= DT_TOP;
        }
        else if ( (lStyle & BS_BOTTOM)==BS_BOTTOM )//y方向,下
        {
            uFormat |= DT_BOTTOM;
        }
        else//缺省,y中
        {
            uFormat |= DT_VCENTER;
        }
        
        return uFormat;
    }

    /**
    *@method   DrawButton
    *@brief    绘制按钮函数
    *    
    *@param    HDC hDC   作图设备句柄
    *@param    RECT itemRect 按钮位置
    *@return   void
    */
    void DrawButton( HDC hDC, RECT /*itemRect*/ )
    {
        HDC hdc = hDC;
        WTL::CDCHandle dc( hDC );

        SetBkMode( hdc, TRANSPARENT );

        LONG lStyle = GetWindowLong( GWL_STYLE );
        BOOL bIsDisabled = ( ( lStyle & WS_DISABLED ) != 0 ); //是否被禁止
        BOOL bIsChecked = ( GetCheck() == BST_CHECKED );

        //判断鼠标是否在按钮上
        CRect rc;
        GetWindowRect( rc );
        
        POINT pt;
        GetCursorPos( &pt );

        Image *pImg = NULL;
        UINT uStateFont = CONTROL_BTN_NORMAL;

        //如果当前处于失效状态
        if( bIsDisabled )
        {
            if( bIsChecked )//如果当前被选择
            {
                pImg = ( GetImage( CONTROL_RDB_DISCHECKED ) != NULL ? GetImage( CONTROL_RDB_DISCHECKED ) : GetImage ( CONTROL_RDB_CHECKED ) );
                uStateFont = CONTROL_RDB_DISCHECKED;  
            }
            else
            {
                pImg = ( GetImage( CONTROL_RDB_DISUNCHECK ) != NULL ? GetImage( CONTROL_RDB_DISUNCHECK ) : GetImage( CONTROL_RDB_DISCHECKED ) );
                uStateFont = CONTROL_RDB_DISUNCHECK;  //CONTROL_RDB_DISUNCHECK
            }
        }
        else
        {
            if( bIsChecked )//如果当前被选择
            {
                pImg = GetImage( CONTROL_RDB_CHECKED );
                uStateFont = CONTROL_RDB_CHECKED;
            }
            else
            {
                pImg = GetImage( CONTROL_RDB_UNCHECK );
                uStateFont = CONTROL_RDB_UNCHECK;
            }
        }

        //如果没有对应状态的图片
        if( pImg == NULL )
        {
            if( m_pCurImage != NULL )
            {
                pImg = m_pCurImage;
            }
            else
            {
                pImg = GetImage( CONTROL_BTN_NORMAL );
                uStateFont = CONTROL_BTN_NORMAL;
            }
        }

        //如果当前图片相同
        if( m_pCurImage != pImg )
        {
            //保存上一个状态的图片
            if( NULL != m_pCurImage)
            {
                m_pLastImage = m_pCurImage;
            }
            else
            {
                m_pLastImage = pImg;
            }
            m_pCurImage = pImg;
        }
        //绘制图片
        Graphics graph( hDC );
        int titleStart = 0;

        if( m_pCurImage != NULL )
        {
            graph.SetPageScale( 1.0 );
            graph.SetPageUnit( UnitPixel ); 
            graph.SetSmoothingMode( SmoothingModeNone );
            titleStart = m_pCurImage->GetWidth( );
            graph.DrawImage( m_pCurImage, 0, 0, titleStart, m_pCurImage->GetHeight() );
        }

        // 显示标题
        TCHAR tstrTitle[MAXRADIOXTXT] = _T("");
        GetWindowText( tstrTitle, MAXRADIOXTXT);

        CRect rtWnd;
        GetWindowRect( &rtWnd );
        ScreenToClient( &rtWnd );

        HFONT hFont = GetStateFont(uStateFont); //得到的Font要求销毁!
        Gdiplus::Font fontTitle(hDC,hFont);

        StringFormat format;
        UINT uStyle = GetButtonStyle();
        if ( BS_PUSHLIKE & uStyle )
        {
            CRect rtfTitle( 0, 0, rtWnd.Width(), rtWnd.Height() );
            format.SetAlignment(StringAlignmentCenter);
            format.SetLineAlignment(StringAlignmentCenter);
            if(!bIsDisabled) //失效状态
            {
                HFONT hOldFont = dc.SelectFont( hFont );
                dc.SetTextColor(m_enableColor.ToCOLORREF());
                dc.DrawText( tstrTitle, -1, &rtfTitle, GetButtonTextFormat( lStyle ) );
                dc.SelectFont( hOldFont );
                //SolidBrush sbTitle(m_enableColor);
                //graph.DrawString( tstrTitle,(int) _tcslen(tstrTitle), &fontTitle, rtfTitle, &format, &sbTitle );
            }
            else
            {
                HFONT hOldFont = dc.SelectFont( hFont );
                dc.SetTextColor(m_unenableColor.ToCOLORREF());
                dc.DrawText( tstrTitle, -1, &rtfTitle, GetButtonTextFormat( lStyle ) );
                dc.SelectFont( hOldFont );
                //SolidBrush sbTitle(m_unenableColor);
                //graph.DrawString( tstrTitle,(int) _tcslen(tstrTitle), &fontTitle, rtfTitle, &format, &sbTitle );
            }
        }
        else
        {   
            CRect rtfTitle( titleStart, 0, rtWnd.Width() - titleStart, rtWnd.Height() );
            format.SetAlignment( StringAlignmentNear );
            format.SetLineAlignment( StringAlignmentCenter );
            if(!bIsDisabled) //失效状态
            {
                HFONT hOldFont = dc.SelectFont( hFont );
                dc.SetTextColor(m_enableColor.ToCOLORREF());
                dc.DrawText( tstrTitle, -1, &rtfTitle, GetButtonTextFormat( lStyle ) );
                dc.SelectFont( hOldFont );
            //  SolidBrush sbTitle(m_enableColor);
            //  graph.DrawString( tstrTitle, (int)_tcslen(tstrTitle), &fontTitle, rtfTitle, &format, &sbTitle );
            }
            else
            {
                HFONT hOldFont = dc.SelectFont( hFont );
                dc.SetTextColor(m_unenableColor.ToCOLORREF());
                dc.DrawText( tstrTitle, -1, &rtfTitle, GetButtonTextFormat( lStyle ) );
                dc.SelectFont( hOldFont );
                //SolidBrush sbTitle(m_unenableColor);
                //graph.DrawString( tstrTitle, (int)_tcslen(tstrTitle), &fontTitle, rtfTitle, &format, &sbTitle );
            }
        }
        graph.ReleaseHDC( hDC );
        DeleteObject( hFont );
    }
};