vc++带下拉列表的button
一、新建一个以CButton类为基类的新类单击“Insert”→“New Class”,建立一个新类。基类设置为CButton,新类起名为CMenuButton。
二、利用自绘方法绘制按钮,主体区显示按钮文本,选择区画一个小箭头
在CMenuButton类中用ClassWizard添加函数:PreSubclassWindow()和DrawItem()。
PreSubclassWindow()函数在建立按钮时执行,可用于做一些准备工作。在这里我给按钮添加自绘属性:
void CMenuButton::PreSubclassWindow()
{
ModifyStyle( 0, BS_OWNERDRAW ); //设置按钮属性为自绘式
CButton::PreSubclassWindow();
}
DrawItem()函数用于绘制按钮,左边绘制按钮文字,作为主体区,右边绘制一个小箭头,作为选择区。实际应用中,可根据具体需要绘制想要的形状和内容。
void CMenuButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC *pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
m_ButRect = lpDrawItemStruct->rcItem; //获取按钮尺寸
int nSavedDC = pDC->SaveDC();
VERIFY( pDC );
DrawButton(pDC); //绘制按钮
pDC->RestoreDC( nSavedDC );
}
其中m_ButRect都是CRect型对象,在头文件中进行定义。DrawButton()为绘制按钮的函数,把它定义在外边的目的是方便用户修改,如果你想改变按钮形状,只需修改DrawButton()函数即可。
void CMenuButton::DrawButton(CDC *pDC)
{
m_LRect.SetRect( m_ButRect.left, m_ButRect.top,
m_ButRect.right-21, m_ButRect.bottom ); //按钮主体区尺寸
m_RRect.SetRect( m_ButRect.right-20, m_ButRect.top,
m_ButRect.right, m_ButRect.bottom ); //按钮选择区尺寸
CPen Pen;
Pen.CreatePen(PS_SOLID, 1, RGB(192,192,192) );
pDC->SelectObject( &Pen );
pDC->FillSolidRect( m_ButRect, m_BackColor ); //画背景
switch( m_State ) //不同状态画不同边框
{
case 0: //正常按钮
pDC->DrawEdge( &m_LRect, BDR_RAISEDINNER, BF_RECT );
pDC->DrawEdge( &m_RRect, BDR_RAISEDINNER, BF_RECT );
break;
case 1: //鼠标进入时的按钮
pDC->DrawEdge( &m_LRect, BDR_RAISEDINNER, BF_RECT );
pDC->DrawEdge( &m_RRect, BDR_RAISEDINNER, BF_RECT );
pDC->MoveTo( m_ButRect.TopLeft() );
pDC->LineTo( m_ButRect.right, m_ButRect.top );
break;
case 2: //单击按钮主体区时的按钮
pDC->DrawEdge( &m_RRect, BDR_RAISEDINNER, BF_RECT );
break;
case 3: //单击按钮选择区时的按钮
pDC->DrawEdge( &m_LRect, BDR_RAISEDINNER, BF_RECT );
break;
}
POINT m_pt[3], m_ptCentre; //箭头坐标(三个顶点)
m_ptCentre = m_RRect.CenterPoint(); //选择区中点位置
m_pt[0].x = m_ptCentre.x-3; //计算箭头坐标
m_pt[0].y = m_ptCentre.y-2;
m_pt[1].x = m_ptCentre.x+4;
m_pt[1].y = m_ptCentre.y-2;
m_pt[2].x = m_ptCentre.x;
m_pt[2].y = m_ptCentre.y+2;
pDC->SelectStockObject( BLACK_BRUSH ); //定义画刷(黑色)
CRgn rgn;
rgn.CreatePolygonRgn( m_pt, 3, ALTERNATE );
pDC->PaintRgn( &rgn ); //画选择区箭头
pDC->SetTextColor( m_ForeColor ); //画主体区文字
pDC->SetBkMode( TRANSPARENT );
pDC->DrawText( m_strText, &m_LRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS);
}
m_State是个标志,其值为0表示正常按钮;1表示鼠标进入按钮,绘制暗线边框;2表示在按钮主体区按下鼠标左键;3表示在按钮选择区按下鼠标左键。
在m_State的不同取值下,绘制不同的按钮边框,可以增加按钮的动态效果。
三、添加鼠标响应函数
在CMenuButton类中用ClassWizard添加函数:OnMouseMove()、OnLButtonDown()、OnLButtonUp()。
OnMouseMove()函数用于响应鼠标移动消息,当鼠标进入按钮时,设置相应标志,并重绘按钮边框,当鼠标离开按钮时,清除标志,恢复原边框。
void CMenuButton::OnMouseMove(UINT nFlags, CPoint point)
{
if( !b_InFlag || GetCapture()!=this ) //鼠标进入按钮
{
b_InFlag = true; //设置进入标志
SetCapture(); //捕获鼠标
m_State = 1; //置按钮状态(1-当前按钮)
if( b_ClickFlag ) //检测单击选择区标志
{
m_Menu.Detach(); //清除打开的菜单
m_Menu.DestroyMenu();
b_ClickFlag = false;
}
Invalidate(); //重绘按钮
}
else
{
if ( !m_ButRect.PtInRect(point) ) //鼠标离开按钮
{
b_InFlag = false; //清除进入标志
ReleaseCapture(); //释放鼠标捕获
b_ClickBut = false; //清除单击标志
m_State = 0; //置按钮状态(0-正常按钮)
if( b_ClickFlag ) //检测单击选择区标志
{
m_Menu.Detach(); //清除打开的菜单
m_Menu.DestroyMenu();
b_ClickFlag = false;
}
Invalidate(); //重绘按钮
}
}
CButton::OnMouseMove(nFlags, point);
}
b_InFlag是个BOOL型量,鼠标进入时设置,离开时清除,目的是防止鼠标在按钮上移动时重复刷新按钮,以避免闪烁。
b_ClickFlag是单击按钮选择区标志,当它为true时,表示弹出菜单已打开,为false时表示菜单未弹出。当菜单已经弹出,而鼠标又移回按钮单击时,应清除菜单。
b_ClickBut是单击按钮主体区标志。
OnLButtonDown()函数响应按钮单击消息,当单击的是按钮主体区时,设置b_ClickBut标志;当单击的是按钮选择区时,要根据单击次数,决定是否弹出菜单。
void CMenuButton::OnLButtonDown(UINT nFlags, CPoint point)
{
if( m_LRect.PtInRect(point) ) //单击按钮主体区
{
m_State = 2; //置按钮状态(2-正常按钮)
b_ClickBut = true; //设置单击按钮标志
Invalidate(); //重绘按钮
}
else if( m_RRect.PtInRect(point) && m_MenuID ) //单击选择区
{
m_State = 3;
b_ClickBut = false; //清除单击按钮标志
Invalidate(); //重绘按钮
b_ClickFlag = !b_ClickFlag; //单击选择区标志
if( b_ClickFlag ) //一次单击,弹出菜单
{
CRect rect = m_RRect;
ClientToScreen(rect); //转换为屏幕坐标
point = rect.BottomRight();
point.x -= rect.Width(); //设置弹出菜单的位置
VERIFY(m_Menu.LoadMenu(m_MenuID)); //装入菜单资源
CMenu* pPopup = m_Menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
CWnd* pWndPopupOwner = this;
while (pWndPopupOwner->GetStyle() & WS_CHILD)
pWndPopupOwner = pWndPopupOwner->GetParent();
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, point.x, point.y, pWndPopupOwner); //弹出菜单
}
else //再次单击,清除菜单
{
m_Menu.Detach();
m_Menu.DestroyMenu();
}
}
CButton::OnLButtonDown(nFlags, point);
}
当单击按钮选择区时,在选择区的左下角弹出菜单,函数中的point是鼠标的屏幕坐标,求菜单位置时也使用屏幕坐标。
m_MenuID是与按钮关联的弹出菜单的ID,它在创建按钮时进行设置。
OnLButtonUp()函数响应按钮弹起消息,这是只要恢复按钮正常状态即可,以产生单击动画效果。
void CMenuButton::OnLButtonUp(UINT nFlags, CPoint point)
{
m_State = 0; //恢复为正常按钮
Invalidate(); //重绘按钮
CButton::OnLButtonUp(nFlags, point);
}
最后请大家注意,有几个类成员的定义没有写清楚,随下面附上
class CMaterialBut : public CButton
{
// Construction
public:
CMaterialBut();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMaterialBut)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
// Implementation
public:
UINT m_MenuID;
COLORREF m_ForeColor;
COLORREF m_BackColor;
bool b_ClickBut;
CString m_strText;
CMenu m_Menu;
bool b_ClickFlag;
bool b_InFlag;
CRect m_RRect;
CRect m_LRect;
int m_State;
void DrawButton(CDC *pDC);
CRect m_ButRect;
virtual ~CMaterialBut();
// Generated message map functions
public:
//{{AFX_MSG(CMaterialBut)
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};