前言

就是窗口上有个按钮,然后鼠标过去按钮就移动。


正文

新建MFC项目
选中基于对话框
用户界面的东西基本用不到
高级功能打印也用不到
其它都正常创建


核心思想跟网上流传的网页版本可能有区别吧,这个原理就是两个按钮,当发现鼠标要触碰到这个按钮就隐藏这个,启用另一个。


首先就是拖俩button控件

然后将左边的按钮属性中 可见 这一属性设为FALSE

那么跑起来的效果就是这样的,看不到左边

然后给这两个控件添加变量,[过程不做演示了,mfc用多了就习惯了]

1
2
3
public:
CButton m_btn_right;
CButton m_btn_left;

对于对话框而言,能第一时间想到触碰按钮的消息,也就是跟鼠标移动有关的。
那么显然是有这么一个消息的OnMouseMove

添加完之后

1
2
3
4
5
6
7
8
9
10
11
void CRunningButtonDlg::OnMouseMove(UINT nFlags, CPoint point){
// TODO: 在此添加消息处理程序代码和/或调用默认值
CRect left, right;
m_btn_right.GetWindowRect(right);
if(right.PtInRect(point) == TRUE){
m_btn_right.ShowWindow(SW_HIDE);
m_btn_left.ShowWindow(SW_SHOW);
}

CDialogEx::OnMouseMove(nFlags, point);
}

原理也简单,就是当鼠标移动到这个控件上的时候,就隐藏right按钮,显示left按钮。

不过在实际run的时候会发现并没有起作用。
遇事不决可以添加调试的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CRunningButtonDlg::OnMouseMove(UINT nFlags, CPoint point){
// TODO: 在此添加消息处理程序代码和/或调用默认值
CRect left, right;
m_btn_right.GetWindowRect(right);
if(right.PtInRect(point) == TRUE){
m_btn_right.ShowWindow(SW_HIDE);
m_btn_left.ShowWindow(SW_SHOW);
}

TRACE("%s(%d):%s %d %d\n", __FILE__, __LINE__,
__FUNCTION__, point.x, point.y);

CDialogEx::OnMouseMove(nFlags, point);
}

可以看到只要鼠标是在这个对话框上移动,xy就会不断发生变化,但是当鼠标进入控件的时候,xy就不会输出了。

原因在于这个消息被按钮控件接收了,判断那里就没法用了,所以对话框无法知道这个鼠标到哪了
所以通过鼠标移动消息无法实现这个效果。


解决方式需要重载这个按钮类。

右击资源视图的项目,选择类向导,在添加类的箭头点击选择MFC类,基类选择为CButton就行,其它的头文件和源文件命名方式看个人习惯吧。

新建完之后照例打开类视图选择这个新建的类,然后在属性中找到消息,继续选择MouseMove

1
2
3
4
5
6
7
8
9
void CMyButton::OnMouseMove(UINT nFlags, CPoint point){
// TODO: 在此添加消息处理程序代码和/或调用默认值
ShowWindow(SW_HIDE);
if(m_pButton != NULL){
m_pButton->ShowWindow(SW_SHOW);
}

CButton::OnMouseMove(nFlags, point);
}

然后修改RunningButtonDlg.h

1
2
3
4
5
6
7
#include "CMyButton.h"

//...

public:
CMyButton m_btn_right;
CMyButton m_btn_left;

将这个按钮改为我们重载之后的按钮类型。

最后在RunningButtonDlg.cpp中初始化的地方关联这两个按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BOOL CRunningButtonDlg::OnInitDialog(){
CDialogEx::OnInitDialog();

// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标

// TODO: 在此添加额外的初始化代码
m_btn_left.m_pButton = &m_btn_right;
m_btn_right.m_pButton = &m_btn_left;


return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}

对话框中的OnMouseMove可以注释掉了

至此效果就完成了。

鼠标移动到其中一个按钮,就会立马hide,然后另一个show。


结语

重载button类,是因为之前的OnMouseMove是基于对话框的,那么控件和对话框之间其实有一个z轴,不是一个平级的关系,所以在对话框中写鼠标移动,当鼠标进入到控件的时候,这个消息则是控件捕捉到的,跟对话框就没了关系。

而重载button类之后,我们鼠标移动的消息是发生在button上,自然就跟对话框没啥关系,所以当鼠标要滑动到其中一个按钮的时候就会立刻响应我们消息中的代码隐藏自身,启用另一个。

也是因为这种特性,所以是点不到这个按钮了。

即便是给按钮加选中之后弹出消息

1
2
3
4
void CRunningButtonDlg::OnBnClickedBtnRight(){
// TODO: 在此添加控件通知处理程序代码
MessageBox(_T("恭喜你点到了"),_T("成功!"));
}

因为俩按钮效果一样,函数就不多写一个了,在列表里关联的时候让左边的按钮也关联这个函数消息就行了

1
2
3
4
5
6
7
BEGIN_MESSAGE_MAP(CRunningButtonDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_MOUSEMOVE()
ON_BN_CLICKED(IDC_BTN_RIGHT, &CRunningButtonDlg::OnBnClickedBtnRight)
ON_BN_CLICKED(IDC_BTN_LEFT, &CRunningButtonDlg::OnBnClickedBtnRight)
END_MESSAGE_MAP()

不过这一步做不做都一样反正点击不到这个按钮,除非你注释掉重载按钮的鼠标移动消息