概述:

CQsComboBox 继承 CComboBox,该控件是有edit 控件 组合而成的复合控件,并且对下列框的背景和框的下拉进行了重绘。

代码实现如下:

#pragma once

#include "QsInclude.h"
#include "QsEdit.h"
#define QS_COMBOX_SYSYSTEM 0x00000001 //ComboBox系统样式
#define QS_COMBOX_OWNERDRAW 0x00000002 //CheckBox自绘样式,默认为自绘样式。


/* CQsComboBox class */
class CQsComboBox :
public CWindowImpl<CQsComboBox, CComboBox>,
public CImageMgrCtrlBase< CQsComboBox >
{
typedef CWindowImpl< CQsComboBox, CComboBox > theBaseClass;
typedef CImageMgrCtrlBase< CQsComboBox> theImageCtrlBaseClass;

Image *m_pCurImage; //当前正在使用的图片
Image *m_pLastImage; //发生状态改变前使用的图片
volatile bool m_bMouseDown; //鼠标左键按下
CQsEdit m_edtEdit; //编辑框控件
CFont font;
DWORD m_dwStyle;

public:
BEGIN_MSG_MAP( CQsComboBox )

MESSAGE_HANDLER( WM_ERASEBKGND, OnEraseBKGnd )
MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
MESSAGE_HANDLER( WM_LBUTTONUP, OnLButtonUp )
MESSAGE_HANDLER( WM_PAINT, OnPaint )
MESSAGE_HANDLER( WM_MOUSEMOVE, OnMouseMove )
MESSAGE_HANDLER( WM_MOUSELEAVE, OnMouseLeave )
CHAIN_MSG_MAP( theImageCtrlBaseClass )
DEFAULT_REFLECTION_HANDLER()

END_MSG_MAP()

/**
*@method CQsComboBox
*@brief CQsComboBox's default constructor.
*
*@return
*/
CQsComboBox():
m_bBtnMouseStatus( CONTROL_CBS_NORMAL ),
m_bLeaveFlag( false )
{
m_uFirstPos = CONTROL_CBS_FIRST;
m_uLastPos = CONTROL_CBS_LAST;
m_dwStyle = QS_COMBOX_OWNERDRAW;
}

/**
*@method ~CQsComboBox
*@brief CQsComboBox's destructor.
*
*@return
*/
virtual ~CQsComboBox()
{
if (NULL != font.m_hFont)
{
DeleteObject(&font);
} }
/**
*@method Create
*@brief
*
*@param HWND hWndParent
*@param ATL::_U_RECT rect = NULL
*@param LPCTSTR szWindowName = NULL
*@param DWORD dwStyle = 0
*@param DWORD dwExStyle = 0
*@param ATL::_U_MENUorID MenuOrID = 0U
*@param LPVOID lpCreateParam = NULL
*@return HWND
*/
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, szWindowName, dwStyle, dwExStyle, MenuOrID, lpCreateParam);
}
/**
*@method SetQSWindowsStyle
*@brief 设置窗口的样式
*
*@param DWORD dwstyle dwstyle 窗口的样式
*@return void
*/void SetQSWindowsStyle(DWORD dwstyle)
{
m_dwStyle = dwstyle;
}
/**
*@method SubclassWindow
*@brief Use this function to subclass one window
*
*@param HWND hWnd subclass binding window handle
*@return BOOL success return TRUE, failed return FALSE
*/
BOOL SubclassWindow( HWND hWnd )
{
BOOL bRet = theBaseClass::SubclassWindow( hWnd );

return bRet;
}
/**
*@method SetQSFont
*@brief 设置文字的大小
*
*@param TCHAR*fontName
*@param int nFontSize
*@param int lfWeight = FW_NORMAL
*@param BYTE fCharSet = ANSI_CHARSET
*@return void
*/
void SetQSFont(LPCTSTR lpszFaceName,int nFontSize,int lfWeight = FW_NORMAL,BYTE fCharSet = DEFAULT_CHARSET)
{
LOGFONT itemFont;
itemFont.lfCharSet = fCharSet;
itemFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
itemFont.lfEscapement = 0;
memset(itemFont.lfFaceName, 0, LF_FACESIZE);
memcpy_s(itemFont.lfFaceName, LF_FACESIZE, lpszFaceName, LF_FACESIZE);
itemFont.lfHeight = nFontSize;
itemFont.lfItalic = FALSE;
itemFont.lfOrientation = 0;
itemFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
itemFont.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;
itemFont.lfQuality = DEFAULT_QUALITY;
itemFont.lfStrikeOut = FALSE;
itemFont.lfUnderline = FALSE;
itemFont.lfWeight = lfWeight;
itemFont.lfWidth = 0;
font = ::CreateFontIndirect( &itemFont );
SetFont(font);
}

protected:

/**
*@method OnPaint
*@brief WM_PAINT message handle function
*
*@param UINT uMsg Message id
*@param WPARAM wParam word param
*@param LPARAM lParam LParam
*@param BOOL& bHandled if message is handled
*@return LRESULT whether function call is success
*/
LRESULT OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/ )
{
if(m_dwStyle&QS_COMBOX_SYSYSTEM)
{
return DefWindowProc(uMsg, wParam, lParam);
}
WTL::CPaintDC paintDC( m_hWnd );

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

CRect rc;
GetWindowRect( rc );

//创建内存作图对象
WTL::CDC dc;
dc.CreateCompatibleDC( paintDC.m_hDC );
WTL::CBitmap memBitmap;
memBitmap.CreateCompatibleBitmap( paintDC.m_hDC, rc.Width(), rc.Height() );
HBITMAP hOldBmp = dc.SelectBitmap( memBitmap );

//还原背景
dc.BitBlt( 0, 0, rc.Width(), rc.Height(), paintDC.m_hDC, 0, 0, SRCCOPY );


WTL::CPen pen;
pen.CreatePen( PS_SOLID, 1, RGB( 133, 173, 215 ) );

//画一个淡蓝色的边框
HPEN hOldPen = dc.SelectPen( pen );

dc.RoundRect( 0, 0, rc.Width(), rc.Height(), 5, 5 );
dc.SelectPen( hOldPen );

COMBOBOXINFO cbInfo;
cbInfo.cbSize = sizeof( COMBOBOXINFO );
GetComboBoxInfo( &cbInfo );

Image *pImg = GetImage( m_bBtnMouseStatus );

if( pImg )
{
Graphics graph( dc.m_hDC );
graph.SetPageScale( 1.0 );
graph.SetPageUnit( UnitPixel );
graph.SetSmoothingMode( SmoothingModeNone );
CRect rcBtn( &cbInfo.rcButton );
rcBtn.left -= 2;
graph.DrawImage( pImg, Rect( rcBtn.left, rcBtn.top, rcBtn.Width(), rcBtn.Height() ),
0, 0, pImg->GetWidth(), pImg->GetHeight(), UnitPixel );
graph.ReleaseHDC( dc.m_hDC );
}

//获得当前EDIT控件的图像
CRect edtRc;
::GetWindowRect( cbInfo.hwndItem, edtRc );

WTL::CDC memDC;
memDC.CreateCompatibleDC( paintDC.m_hDC );
WTL::CBitmap edtBmp;
edtBmp.CreateCompatibleBitmap( paintDC.m_hDC, edtRc.Width(), edtRc.Height() );
HBITMAP hEdtOld = memDC.SelectBitmap( edtBmp.m_hBitmap );

SendMessage( WM_PRINT, (WPARAM)memDC.m_hDC,
(LPARAM)PRF_NONCLIENT | PRF_CLIENT | PRF_CHILDREN | PRF_CHECKVISIBLE );

edtRc = CRect( cbInfo.rcItem );
dc.BitBlt( edtRc.left, edtRc.top, edtRc.Width(), edtRc.Height(), memDC.m_hDC,
edtRc.left, edtRc.top, SRCCOPY );

memDC.SelectBitmap( hEdtOld );

//提交图像
paintDC.BitBlt( 0, 0, rc.Width(), rc.Height(), dc.m_hDC, 0, 0, SRCCOPY );

dc.SelectBitmap( hOldBmp );

dc.DeleteDC();
memBitmap.DeleteObject();
memDC.DeleteDC();
pen.DeleteObject();
edtBmp.DeleteObject();

return 0;
}

/**
*@method OnLButtonDown
*@brief WM_PAINT message handle function
*
*@param UINT uMsg Message id
*@param WPARAM wParam word param
*@param LPARAM lParam
*@param BOOL& bHandled if message is handled
*@return LRESULT whether function call is success
*/
LRESULT OnLButtonDown( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled )
{
m_bMouseDown = true;

bHandled = FALSE;
return 0;
}

/**
*@method OnLButtonUp
*@brief WM_PAINT message handle function
*
*@param UINT uMsg Message id
*@param WPARAM wParam word param
*@param LPARAM lParam LParam
*@param BOOL& bHandled if message is handled
*@return LRESULT whether function call is success
*/
LRESULT OnLButtonUp( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled )
{
m_bMouseDown = false;

bHandled = FALSE;
return 0;
}

/**
*@method OnEraseBKGnd
*@brief WM_PAINT message handle function
*
*@param UINT uMsg Message id
*@param WPARAM wParam word param
*@param LPARAM lParam LParam
*@param BOOL& bHandled if message is handled
*@return LRESULT whether function call is success
*/
LRESULT OnEraseBKGnd( UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/ )
{
//not draw background

return 0;
}

/**
*@method OnMouseMove
*@brief WM_PAINT message handle function
*
*@param UINT uMsg Message id
*@param WPARAM wParam word param
*@param LPARAM lParam LParam
*@param BOOL& bHandled if message is handled
*@return LRESULT whether function call is success
*/
LRESULT OnMouseMove( UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
POINT pos;
pos.x = GET_X_LPARAM(lParam);
pos.y = GET_Y_LPARAM(lParam);

COMBOBOXINFO cbInfo;
cbInfo.cbSize = sizeof( COMBOBOXINFO );
GetComboBoxInfo( &cbInfo );
CRect rc( cbInfo.rcButton );

//判断当前鼠标是否在按钮范围内
if( rc.PtInRect( pos ) )
{
//如果鼠标左键被按下
if( ( ( wParam & MK_LBUTTON ) == MK_LBUTTON ) &&
( m_bBtnMouseStatus != CONTROL_CBS_MOUSEDOWN ) &&
GetImage( CONTROL_CBS_MOUSEDOWN ) )
{
m_bBtnMouseStatus = CONTROL_CBS_MOUSEDOWN;
Invalidate();
}
//否则,将状态设置为鼠标进入状态
else if( ( m_bBtnMouseStatus != CONTROL_CBS_MOUSEIN ) &&
GetImage( CONTROL_CBS_MOUSEIN ) )
{
m_bBtnMouseStatus = CONTROL_CBS_MOUSEIN;
Invalidate();
}
}
else if( ( m_bBtnMouseStatus != CONTROL_CBS_NORMAL ) )
{
m_bBtnMouseStatus = CONTROL_CBS_NORMAL;
Invalidate();
}

//如果鼠标状态不为正常状态,则启动鼠标离开事件检查
if( ( m_bBtnMouseStatus != CONTROL_CBS_NORMAL ) && !m_bLeaveFlag )
{
// 启动鼠标离开时间
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
TrackMouseEvent(&tme);
}

bHandled = FALSE;
return 0;
}

/**
*@method OnMouseLeave
*@brief WM_PAINT message handle function
*
*@param UINT uMsg Message id
*@param WPARAM wParam word param
*@param LPARAM lParam LParam
*@param BOOL& bHandled if message is handled
*@return LRESULT whether function call is success
*/
LRESULT OnMouseLeave( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/ )
{
if(m_dwStyle&QS_COMBOX_SYSYSTEM)
{
return DefWindowProc(uMsg, wParam, lParam);
}
if( ( m_bBtnMouseStatus != CONTROL_CBS_NORMAL ) )
{
m_bBtnMouseStatus = CONTROL_CBS_NORMAL;
Invalidate();
}

return 0;
}

private:
volatile UINT m_bBtnMouseStatus; //当前鼠标状态 CONTROL_CBS_NORMAL:鼠标未进入 1:鼠标进入 2:鼠标左键按下
volatile bool m_bLeaveFlag; //鼠标离开事件检查启动标志 false:未启动 true:已启动

};