概述:
CQsRadioBox基础与 CButton,通过自绘来实现 实现Radion Box的效果,通过BST_CHECKED 属性来实现。
代码实现如下:
#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 );
}
};