VC自定义图片按钮控件的实现

要求:

1.动态生成一个图片按钮,函数输入两幅图片的ID,及按钮坐标和大小,最为重要的是指定单击它要向父窗口传递的消息值(自定义)

2.当鼠标在经过按钮上时图片按钮变为另外一幅图,跟正常状态下的图像形成对比

3.当鼠标单击这个按钮,父窗口得到初始化时给这个窗口指定的消息值,以便在有多个按钮存在时进行区分响应

过程:

1.从CButton类派生CMyBtn类,增加如下变量:

enum {STATE_MOUSEON, STATE_NORMAL}; // 定义按钮状态
CBitmap m_pBmp1, m_pBmp2;//Load两幅图片
CRect m_Rc; //保存按钮客户区
int m_State; // 按钮所处状态 为enum的两个值,代表鼠标在按钮上和正常情况下
BOOL m_IsTimerOn; // 定时器开否,用于判断鼠标跟按钮的相对位置
POINT m_CursorPos; // 鼠标位置

2.因为在动态创建自定义按钮的时候,要指定图片ID、及按钮坐标大小、消息值,所以重载CButton类的Create函数。

BOOL CMyBtn::Create(UINT IDBITMAPNOMAL, UINT IDBITMAPMOUSEON, UINT msg,
      LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) 
      // 重载Create,指定按钮所处两种状态应显示的图片,及单击时向父窗口传递的消息
{
// TODO: Add your specialized code here and/or call the base class
m_pBmp1.LoadBitmap(IDBITMAPNOMAL);
m_pBmp2.LoadBitmap(IDBITMAPMOUSEON);

// 变量初始化
m_IsTimerOn = FALSE;
m_State = STATE_NORMAL;
m_message = msg;

return CButton::Create(lpszCaption, dwStyle, rect, pParentWnd, nID);
}

3.要想重绘按钮要设定按钮类型为BS_OWNERDRAW,重载PreSubclassWindow函数

void CMyBtn::PreSubclassWindow() 
{
// TODO: Add your specialized code here and/or call the base class
ModifyStyle(0, BS_OWNERDRAW|BS_PUSHBUTTON);
CButton::PreSubclassWindow();
}

4.然后对按钮进行重绘,重载DrawItem函数

void CMyBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
// TODO: Add your code to draw the specified item
GetClientRect(&m_Rc); // 得到按钮窗口的有效矩形区域

CDC *pDc = CDC::FromHandle(lpDrawItemStruct->hDC); // 取得按钮控件客户区域的设备变量指针

CDC MemDc;
MemDc.CreateCompatibleDC(pDc);

CBitmap * pBmp = NULL;

if(STATE_NORMAL == m_State)
{
   pBmp = MemDc.SelectObject(&m_pBmp1);
}
if(STATE_MOUSEON == m_State)
{
   pBmp = MemDc.SelectObject(&m_pBmp2);
}

pDc->BitBlt(m_Rc.left, m_Rc.top, m_Rc.right, m_Rc.bottom, &MemDc, 0, 0, SRCCOPY);

pBmp = MemDc.SelectObject(pBmp);
}

5.判断鼠标是否在按钮区域内的方法是,如果鼠标在按钮区域移动,则设定一个计时器,对鼠标位置进行跟踪检测,如果在则设定m_State为STATE_MOUSEON,否则设为STATE_NORMAL。然后无效整个客户区进行重绘。

void CMyBtn::OnMouseMove(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
if(!m_IsTimerOn)
{
   SetTimer(10000, 100, NULL);
   m_IsTimerOn = TRUE;
}
CButton::OnMouseMove(nFlags, point);
}

void CMyBtn::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
CRect rect;
GetWindowRect(&rect); // 得到按钮客户区域的屏幕坐标位置
GetCursorPos(&m_CursorPos); // 得到鼠标的屏幕坐标位置

if(rect.PtInRect(m_CursorPos)) // 如果鼠标在按钮的客户区内
{
   if(STATE_MOUSEON != m_State)
   {
    m_State = STATE_MOUSEON;
    Invalidate();
   }
}

else // 鼠标离开按钮客户区
{
   if(STATE_NORMAL != m_State)
   {
    m_State = STATE_NORMAL;
    Invalidate();
   }

   KillTimer(nIDEvent);
   m_IsTimerOn = FALSE;
}

CButton::OnTimer(nIDEvent);
}

6.响应单击操作,向父窗口传递m_message消息,其值在Create时由父窗口指定。

void CMyBtn::OnLButtonUp(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
GetParent()->PostMessage(m_message);
CButton::OnLButtonUp(nFlags, point);
}

附:VC自定义消息响应的实现

第一步:定义消息。开发Windows95应用程序时,Microsoft推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。

第二步:实现消息处理函数。该函数使用WPRAM和LPARAM参数并返回LPESULT。

LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
// TODO: 处理用户自定义消息
...
return 0;
}
第三步:在类头文件的AFX_MSG块中说明消息处理函数:

class CMainFrame:public CMDIFrameWnd
{
...
// 一般消息映射函数
protected:
// {{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
}
第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

如果用户需要一个整个系统唯一的消息,可以调用SDK函数RegisterWindowMessage并使用ON_REGISTER_MESSAGE宏指令取代ON_MESSAGE宏指令,其余步骤同上。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值