要求:
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()