按钮的互斥事件实现

antique_star 2017-03-29 04:12:32

各位前辈,在做折叠菜单中的二级菜单的鼠标事件时,遇到互斥事件。


实现的效果就是,处于点击状态的按钮取消它的背景变换。同时恢复上一个的鼠标事件正常。
我做的效果,所有二级菜单按钮每一个在第一次点击时都达到了效果。但是第二次点击时,按下的效果就没有了,鼠标一离开就触发了离开事件。假象就是好像第二次点击没有解除掉它的离开与进入事件。


如果我的鼠标事件有问题,那为什么第一次又能正常解除它的离开与进入事件呢?
百思不得其解,请大神们看看代码哪里有问题。


先说明几个问题:
1、折叠菜单一级按钮我没有弄互斥效果,是通过两个Image和改变文本颜色来提醒用户;
2、折叠菜单实现原理:用FlowLayOutPanel的流式布局做主容器,每个一级菜单按钮下放一个Panel,一级菜单按钮的Tag与Panel设为相同索引号,在一级菜单的点击事件中循环容器中Panel控件的Tag,如果相同的就将Panel的高度设为0,达到折叠效果。
3、在二级菜单的Panel容器中放置按钮,这些按钮要求互斥,互斥的实现是变换背景图片。

代码如下:


public partial class SubMenuBt : Button
{
public SubMenuBt()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.Font = new Font("方正书宋简体", 10F);
this.Image = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单图标2.png");
this.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.BackgroundImageLayout = ImageLayout.Stretch;
this.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Normal.png");
this.Size = new Size(150, 25);
this.Margin = new Padding(0,1,0,0);
this.Checked = false;
this.MouseEnter +=new EventHandler(SubMenuBt_MouseEnter);
this.MouseLeave +=new EventHandler(SubMenuBt_MouseLeave);
this.MouseDown +=new MouseEventHandler(SubMenuBt_MouseDown);
this.Click +=new EventHandler(SubMenuBt_Click);
}

public bool Checked { get; set; }

private void SubMenuBt_Click(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
foreach (Control c in sbt.Parent.Controls)
{
if (c is SubMenuBt && c != sbt)
{
SubMenuBt bt = c as SubMenuBt;
if (bt.Checked == true)
{
bt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Normal.png");
bt.MouseEnter += new EventHandler(SubMenuBt_MouseEnter);
bt.MouseLeave += new EventHandler(SubMenuBt_MouseLeave);
bt.Checked = false;
}
}
}

if (sbt.Checked == false)
{
sbt.MouseEnter -= new EventHandler(SubMenuBt_MouseEnter);
sbt.MouseLeave -= new EventHandler(SubMenuBt_MouseLeave);
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Down.png");
sbt.Checked = true;
}
}

private void SubMenuBt_MouseEnter(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景In.png");
}

private void SubMenuBt_MouseLeave(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Normal.png");
}

private void SubMenuBt_MouseDown(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Down.png");
}
}

初始状态效果图: 点开状态效果图:



请大神们帮忙看看哪里需要完善!拜托了。
...全文
750 8 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
xuggzu 2017-03-30
  • 打赏
  • 举报
回复
楼主这次的描述才算是简洁明了。至于你的要求解决方法其实很简单: 给每个item设个标志,记录单击过没。单击了,置位。鼠标移动时循环一圈判读每个item此标志,根据标志值做楼主说的状态变换。再在item的点击事件中清除所有item标志,再将此次单击的item标志置位即可。
antique_star 2017-03-29
  • 打赏
  • 举报
回复
根据大家的建议,我改了一下代码,果然变得简单得多,思路也简洁明了。
图中蓝绿色是已经被用户点击了的效果,黄绿色是鼠标移入的效果:


代码改成这样了:

public partial class SubMenuBt : Button
{
public SubMenuBt()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.Font = new Font("方正书宋简体", 10F);
this.Image = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单图标2.png");
this.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.BackgroundImageLayout = ImageLayout.Stretch;
this.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Normal.png");
this.Size = new Size(150, 25);
this.Margin = new Padding(0,1,0,0);
this.Checked = false;
this.MouseEnter +=new EventHandler(SubMenuBt_MouseEnter);
this.MouseLeave +=new EventHandler(SubMenuBt_MouseLeave);
this.Click +=new EventHandler(SubMenuBt_Click);
}

public bool Checked { get; set; }

private void SubMenuBt_Click(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Down.png");
sbt.Checked = true;

foreach (Control c in sbt.Parent.Controls) //这句我还是要用,要不然我不知道怎么恢复上一个的状态
{
if (c is SubMenuBt)
{
SubMenuBt bt = c as SubMenuBt;
if (bt.Checked == true && bt!= sbt)
{
bt.Checked = false;
bt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Normal.png");
break;
}
}
}
}

private void SubMenuBt_MouseEnter(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
if (sbt.Checked == false)
{
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景In.png");
}
}

private void SubMenuBt_MouseLeave(object sender, EventArgs e)
{
SubMenuBt sbt = sender as SubMenuBt;
if (sbt.Checked == false)
{
sbt.BackgroundImage = Image.FromFile(@"E:\C#项目文件\控件测试图片\动态菜单图片\二级菜单背景Normal.png");
}
}
}


三楼的大神,可别骂我,修改后的代码还是有一句你不认可的逻辑。
您的意思,是不是在某个地方放一个监视器的功能,时刻盯着每个按钮的状态Checked,状态变,则背景同时变?
如果是这样你太高看我了,以我目前的水平还不敢奢谈这种技术。
不怕你笑话,到目前我都还没过委托的关,
被绕得云里雾里的。


antique_star 2017-03-29
  • 打赏
  • 举报
回复
引用 1 楼 stherix 的回复:
实现的效果就是,处于点击状态的按钮取消它的背景变换。同时恢复上一个的鼠标事件正常。 这种实现方法并不好 鼠标事件钩子没必要反复挂载和卸载 你不如在鼠标事件里判断本按钮的按下状态,如果是按下状态就不对背景图片进行操作
您的话语不多,但说到了点子上,我想我该换换思路了。
引用 2 楼 xuggzu 的回复:
楞没看明白,楼主描述能不能简洁点……
VS控件工具栏知道不?移动鼠标选择控件是不是颜色变换,但单击后,鼠标移走又回到刚才单击的控件菜单上,是不是颜色还在变换?我觉得这种效果不好,我需要的是,单击后的菜单定格不变色,只在其他未选的菜单上变色,让用户一直知道刚才选了什么,直到下一个单击时,才恢复之前菜单的变色效果。
引用 4 楼 sp1234 的回复:
可能你需要花很长时间才能明白,也可能一下自己明白了,就看你自己了。 1. 你只要根据1、2个数据属性来控制就行了。 2. 协调和切换按钮组的状态,应该是上层宿主程序的职责,不要越俎代庖。每一个按钮都越俎代庖,不乱才怪。
又见到专家您熟悉面孔了,说来惭愧。其实您早在我第一个求助贴中就骂过我本末倒置了。 今天再次被你这么一说,我都快脸红了。 我呢就是个小白,而且是个今年刚满40岁的小白,我脸皮厚,不怕被骂。 只要技术好,人品好,谁都是我的前辈。 今年开始搞业余编程学习的,我的本业是财务负责人。 因为之前与数据打交道多了,所以为了提高效率,就依托Excel的VBA搞了一些二次开发的小工具,挺实用的。 但总是觉得不伦不类。 所以想着搞一门语言, 稀里糊涂就选了Csharp。 不怕您笑话,我完全不是一个程序员。没有任何基础,所以根本没有什么套路,能实现效果就行。 顶多就是个土匪、草包、杂牌军罢了。别误会,我并不是生气,事实就是这样, 我喜欢被你们专业人士吐槽,要不然我怎么能进步呢? 我到今天所有的学习成果全来自百度, 连书都舍不得买一本,太贵了,最少90以上一本。 更不说去上什么培训班了。 我知道,科班出身不是白混的,你们的理念就是权威。 我会努力的。 另外上次你发表回复在我结贴之后,没能给到你分,这次一并补上。呵呵。
  • 打赏
  • 举报
回复
按钮被点击,改变自己的 Checked 属性即可! 按钮的背景图,根自己的 Check 事件关联,跟Checked相关。而不是跟底层的什么 MouseEnter、MouseLeave等事件关联。不是跟底层的概念相关,而是跟高层的 Checked 数据相关。把逻辑往高处重构一下,代码也就简单了。 对于按钮组的“切换”机制,不是任何一个按钮的职责。你应该把切换Checked 状态的代码从这个类中移除,在上层代码中写上3、4行代码来切换 Checked 状态足矣。
  • 打赏
  • 举报
回复
可能你需要花很长时间才能明白,也可能一下自己明白了,就看你自己了。 1. 你只要根据1、2个数据属性来控制就行了。 2. 协调和切换按钮组的状态,应该是上层宿主程序的职责,不要越俎代庖。每一个按钮都越俎代庖,不乱才怪。
  • 打赏
  • 举报
回复
少写代码,才是真正会写代码的人! 不要搞
foreach (Control c in sbt.Parent.Controls)
这种越俎代庖的想当然地逻辑。这种代码应该用在 Automation 测试程序中,而不是用在正式产品中。 一个按钮显示什么状态的背景图,用它的 Checked 来关联就行了。一个按钮不要去想当然去捕获什么“上级控件的其它下级控件”这种逻辑。 对于它的宿主控件,假设需要切换,那么监听所有按钮的 Check 事件,并且在处理时把其它所有按钮的 Checked 属性设展为false,那么其它按钮也就自动改变背景了。这样写出来的代码少、准确。 编程要少写代码,头脑也是一样。你纠缠到底层的 MouseEnter、MouseLeave等等东西,在上面大做文章,不如把更多精力放在高层次的 Checked 上,放在高层数据的业务逻辑上,而不是放在底层机制上。这样写出来的代码就少、且准确。
xuggzu 2017-03-29
  • 打赏
  • 举报
回复
楞没看明白,楼主描述能不能简洁点……
stherix 2017-03-29
  • 打赏
  • 举报
回复
实现的效果就是,处于点击状态的按钮取消它的背景变换。同时恢复上一个的鼠标事件正常。 这种实现方法并不好 鼠标事件钩子没必要反复挂载和卸载 你不如在鼠标事件里判断本按钮的按下状态,如果是按下状态就不对背景图片进行操作

111,094

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

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