MFC 光标停靠客户区的某个区域热点效果是如何实现的?

shmiloveyou 2013-12-31 06:24:56

上图红色方框中的客户区区域光标不停靠时的效果

红色方框中光标停靠时的效果

请问这种效果是如何实现的?虽然考虑过用WM_MOUSEHAVOR和WM_MOUSELEAFE这两个消息,但是没有什么思路!请大家指点一二,有实例更好。谢谢!
...全文
190 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
shmiloveyou 2014-01-02
  • 打赏
  • 举报
回复
CHotBtn类中功能还不是很齐全,可以为其添加更多属性,方法供外部设置、控制。对于属性设置,比如按钮的“普通状态的位图”、“光标停靠的位图”都可以通过增加的函数和属性进行通用性的设置,而不是像我下面的代码那样针对某个按钮。对于控制方面,比如单击处理,可以为CHotBtn类增加一个获取回调函数的成员函数,而不是如我那样在CMainDlg类中的OnCmdMsg函数中处理对应CHotBtn对象的ID来实现单击事件的处理(很不合理),这个应该用回调函数解决。 下面的代码只解决了效果的实现问题,没有解决CHotBtn类的通用性问题: HotBtn.h
class CHotBtn : public CButton
{
	DECLARE_DYNAMIC(CHotBtn)

public:
	CHotBtn();
	virtual ~CHotBtn();

public:  //Attributs
	BOOL _bMouseTrack;  //是否跟踪鼠标开关变量

protected:
	DECLARE_MESSAGE_MAP()
public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg void OnMouseLeave();
	afx_msg void OnMouseHover(UINT nFlags, CPoint point);
};
HotBtn.cpp
IMPLEMENT_DYNAMIC(CHotBtn, CButton)

CHotBtn::CHotBtn()
{
	_bMouseTrack = TRUE;  //是否跟踪鼠标开关变量
}

CHotBtn::~CHotBtn()
{
}


BEGIN_MESSAGE_MAP(CHotBtn, CButton)
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSELEAVE()
	ON_WM_MOUSEHOVER()
END_MESSAGE_MAP()



// CHotBtn 消息处理程序


#define DIRTOOLMENUID 8030

void CHotBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	// TODO:  添加您的代码以绘制指定项
	CRect rect;												//声明区域对象
	GetClientRect(rect);											//获得按钮的客户区域
	CDC dc;													//声明设备上下文
	dc.Attach(lpDrawItemStruct->hDC);								//设置设备上下文

	//CHotBtn类中申明一个结构体和设置结构体的函数,提供外部绘制信息
	//如此就可以使下面的代码通用。  注意OnMouseHover函数也要改
	if(lpDrawItemStruct->CtlID == DIRTOOLMENUID/*=8030*/)  //DirToolMenu控件
	{
		CRect rect;
		GetClientRect(&rect);
		CDC dcMem;
		dcMem.CreateCompatibleDC(&dc);
		CBitmap bmpBackground;
		bmpBackground.LoadBitmap(MAKEINTRESOURCE(IDB_BITMAP1));
		
		CBitmap *pbmpOld = dcMem.SelectObject(&bmpBackground);
		dc.SetStretchBltMode(COLORONCOLOR);
		dc.BitBlt(0,0,66,22,&dcMem,0,2,SRCCOPY);

		dcMem.SelectObject(pbmpOld);
		bmpBackground.DeleteObject();
		dcMem.DeleteDC();
	}
}


void CHotBtn::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	TRACKMOUSEEVENT csTME;

	csTME.cbSize = sizeof(csTME);
	csTME.dwFlags = TME_LEAVE | TME_HOVER; //Mouse leave and hover
	csTME.hwndTrack = m_hWnd;  //指定要追踪的窗口
	csTME.dwHoverTime = 2; //鼠标在按钮上停留超过10ms才认为状态为Hover
	::_TrackMouseEvent(&csTME); //开启windows的WM_MOUSELEAVE,WM_MOUSEHOVER事件支持

	_bMouseTrack = FALSE; //若已经追踪,则停止追踪
	CButton::OnMouseMove(nFlags, point);
}


void CHotBtn::OnMouseLeave()
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	_bMouseTrack = TRUE;
	Invalidate(TRUE);
	CButton::OnMouseLeave();
}


void CHotBtn::OnMouseHover(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CRect rect;												//声明区域对象
	GetClientRect(rect);
	CDC dc;
	CDC *getdc = GetDC();											//获得按钮的客户区域												//声明设备上下文
    HDC hdc = HDC(*getdc);
	dc.Attach(hdc);								//设置设备上下文

	CBitmap bitmap;
	bitmap.LoadBitmap(MAKEINTRESOURCE(IDB_BITMAP2));
	CDC *mdc = new CDC();
	mdc->CreateCompatibleDC(&dc);  //用hdc创建与内存兼容的设备句柄mdc
	mdc->SelectObject(&bitmap);
	dc.BitBlt(0,0,36,22,mdc,0,2,SRCCOPY);
	delete mdc;

	//MessageBox(_T("MouseHover"));
	CButton::OnMouseHover(nFlags, point);
}
shmiloveyou 2014-01-01
  • 打赏
  • 举报
回复
引用 1 楼 worldy 的回复:
鼠标移动到相应位置时,以某种显示形式重画该区域,鼠标离开回复原来原来显示,鼠标点击执行你期望的动作


引用 3 楼 bjtbjt 的回复:


引用 4 楼 GLSC_CENA 的回复:


引用 7 楼 gz_qmc 的回复:


走出误区,设计思路油然而生,在“#8”楼中我分析清问题后,客户区热点效果终于实现了:
普通状态

光标停靠状态:


项目工程整理好后,我会上传到csdn的下载资源中,并在此帖中提供下载地址,希望给遇到同样困惑的朋友提供一点帮助。在此谢谢上面的各位同仁的帮助,谢谢。

shmiloveyou 2013-12-31
  • 打赏
  • 举报
回复
引用 6 楼 xurlhwhb 的回复:
http://www.cnblogs.com/greatverve/archive/2013/02/06/TRACKMOUSEEVENT.html
谢谢您,这篇博文我先前看过,也有过使用经验。我现在发现自己掉入了一个误区: 1.其实用从CButton派生出来的Button类使用自定义WM_MOUSEHOVER、WM_MOUSELEAVE消息配合WM_MOUSEMOVE消息完全可以解决客户区某个区域的热点问题(达到我提问中的效果)。 2.而这个区域就用派生的Button类来创建,然后重绘Button的风格,使其不具有单击时的3D效果、焦点效果、边框,如此给Button加上位图就可以和背景完美的融合,使其看不出来是Button控件。此时的Button区域就只有两种状态:普通状态 光标停靠状态, 单击状态(由于Button没有3D效果、焦点效果、边框)被停靠状态代替,但是单击事件依然能执行。 开始测试这个效果时,就是因为没有解决第2点,从而和预期的效果差距甚远。
gz_qmc 2013-12-31
  • 打赏
  • 举报
回复
响应鼠标进入和离开区域的事件 更换底色或图片一次
xurlhwhb 2013-12-31
  • 打赏
  • 举报
回复
http://www.cnblogs.com/greatverve/archive/2013/02/06/TRACKMOUSEEVENT.html
shmiloveyou 2013-12-31
  • 打赏
  • 举报
回复
引用 3 楼 bjtbjt 的回复:
1 把下面代码加进对话框的WM_MOUSEMOVE的消息响应中
TRACKMOUSEEVENT tme;
tme.cbSize=sizeof(TRACKMOUSEEVENT);
tme.dwFlags=TME_HOVER | TME_LEAVE;
tme.dwHoverTime=HOVER_DEFAULT;
tme.hwndTrack=m_hWnd;
if(!_TrackMouseEvent(&tme))
    AfxMessageBox("鼠标事件捕捉失败!");
你这个m_hWnd如果指定的是主对话框句柄,就只能处理光标进入主对话框和离开住对话框这个两个事件,我要的是光标停靠主对话框的客户区(Client)某个部分就引发热点,离开这个部分就恢复。请看下“#2”楼中我的疑虑部分,希望你能提供好的解决方案。谢谢
这个娜戒海了 2013-12-31
  • 打赏
  • 举报
回复
引用 3 楼 bjtbjt 的回复:
1 把下面代码加进对话框的WM_MOUSEMOVE的消息响应中
TRACKMOUSEEVENT tme;
tme.cbSize=sizeof(TRACKMOUSEEVENT);
tme.dwFlags=TME_HOVER | TME_LEAVE;
tme.dwHoverTime=HOVER_DEFAULT;
tme.hwndTrack=m_hWnd;
if(!_TrackMouseEvent(&tme))
    AfxMessageBox("鼠标事件捕捉失败!");
2 然后你直接可以用下面代码在PreTranslateMessage函数中接收,不需要自己写WM_MOUSELEASE和WM_MOUSEHOVER消息的响应函数(当然你要自己写也行):
if(pMsg->message==WM_MOUSELEAVE) 
    AfxMessageBox("mouse leave");
return CDialog::PreTranslateMessage(pMsg);

或者
手动添加消息响应WM_MOUSELEASE。
.h
afx_msg LRESULT OnMouseLeave(WPARAM wParam,LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam,LPARAM lParam);
.cpp
ON_MESSAGE(WM_MOUSELEAVE,OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER,OnMouseHover)

LRESULT CMyButton::OnMouseLeave(WPARAM wParam,LPARAM lParam)
{
m_bMouseOver=FALSE;
m_bTrack=FALSE;

InvalidateRect(NULL);
return 0;
}

LRESULT CMyButton::OnMouseHover(WPARAM wParam,LPARAM lParam)
{
m_bMouseOver=TRUE;

InvalidateRect(NULL);
return 0;
}
5t4rk 2013-12-31
  • 打赏
  • 举报
回复
1 把下面代码加进对话框的WM_MOUSEMOVE的消息响应中
TRACKMOUSEEVENT tme;
tme.cbSize=sizeof(TRACKMOUSEEVENT);
tme.dwFlags=TME_HOVER | TME_LEAVE;
tme.dwHoverTime=HOVER_DEFAULT;
tme.hwndTrack=m_hWnd;
if(!_TrackMouseEvent(&tme))
    AfxMessageBox("鼠标事件捕捉失败!");
2 然后你直接可以用下面代码在PreTranslateMessage函数中接收,不需要自己写WM_MOUSELEASE和WM_MOUSEHOVER消息的响应函数(当然你要自己写也行):
if(pMsg->message==WM_MOUSELEAVE) 
    AfxMessageBox("mouse leave");
return CDialog::PreTranslateMessage(pMsg);

或者
手动添加消息响应WM_MOUSELEASE。
.h
afx_msg LRESULT OnMouseLeave(WPARAM wParam,LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam,LPARAM lParam);
.cpp
ON_MESSAGE(WM_MOUSELEAVE,OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER,OnMouseHover)

LRESULT CMyButton::OnMouseLeave(WPARAM wParam,LPARAM lParam)
{
m_bMouseOver=FALSE;
m_bTrack=FALSE;

InvalidateRect(NULL);
return 0;
}

LRESULT CMyButton::OnMouseHover(WPARAM wParam,LPARAM lParam)
{
m_bMouseOver=TRUE;

InvalidateRect(NULL);
return 0;
}
shmiloveyou 2013-12-31
  • 打赏
  • 举报
回复
引用 1 楼 worldy 的回复:
鼠标移动到相应位置时,以某种显示形式重画该区域,鼠标离开回复原来原来显示,鼠标点击执行你期望的动作
如何实现判断到达这个区域?在OnMouseMove函数中判断当前传进来的参数point是否在这个区域中?能有比这个方法好的判断吗?因为WM_MOUSEMOVE消息发送很频繁,如果这样做就会有很大的处理量,如果有很多这样的热点效果处理岂不是有很多的判断要处理?请帮我解决这个困惑,能提供示例更好,谢谢!
worldy 2013-12-31
  • 打赏
  • 举报
回复
鼠标移动到相应位置时,以某种显示形式重画该区域,鼠标离开回复原来原来显示,鼠标点击执行你期望的动作

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧