C# Winform 事件阻止问题 在一个事件没结束之前,Click事件再不响应 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 bool IsPress = false;private void button1_Click(object sender, EventArgs e) { if(!IsPress) { IsPress = true; i++; Thread.Sleep(2000); button1.Text = i.ToString(); IsPress = false; } } button1.Enabled = false;button1.Enabled = true;说说为什么不好用 也可以这么用吧private void button1_Click(object sender, EventArgs e) { if(button1.Enabled) { button1.Enabled = false; i++; Thread.Sleep(2000); button1.Text = i.ToString(); button1.Enabled = true; } } 其实你那个是因为界面没来得及响应那个Enabled改变事件造成的,可以在Enabled改变后加入Application.DoEvent();这样做肯定没问题,不过最好的做法是取消事件,写法如下:private void button1_Click(object sender, EventArgs e){ button1.Click -= button1_Click; i++; Thread.Sleep(2000); button1.Text = i.ToString(); //注意,如果代码执行过程可能有异常发生, //必须用try-finally语句, //并将此行代码加入到finally中执行 button1.Click += button1_Click;} 虽然-= += 很优雅但按楼主的要求,不是很赞成使用-= +=内部要处理的操作绝对比Enabled属性,或者标志量要多还是使用Enabled属性或者标志量来处理吧 上面是从技术方面来说下面从产品方面来说:按钮不给点,就应该设置成Enabled = false如果Enabled = true,用户点了又没反应,难免会有用户抱怨了:Y的,什么意思啊,Enabled = true,点了又没反应,是不是bug啊 这个问题的关键在于,由于是UI线程去处理事务,这时候去点击按钮,消息会一直在Windows的消息队列中等待,直到UI线程空闲时再去引发Click事件。所以这种情况下不管在Click事件中判断标志量还是解除事件注册,都没有用,因为再次进来到这里时标志量或者事件都已经还原了。所以要解决这个问题,要么在事件处理结束前把Windows消息队列中按钮的单击消息清除掉,要么用多线程去处理事务,让UI线程立即返回,以响应下一个Click消息,这时就可以通过标志量或者事件去控制按钮是否响应事件了。第一种方案没研究过,第二种就简单多了。private void button1_Click(object sender, EventArgs e){ if (button1.Enabled) { button1.Enabled = false; Thread t = new Thread(() => { i++; Thread.Sleep(2000); button1.Invoke(new Action(() => { button1.Text = i.ToString(); button1.Enabled = true; })); }); t.IsBackground = true; t.Start(); }} 有没有什么设定UI线程去处理事务 的时候,消息呢?不过这样试验过了倒是好用private void button1_Click(object sender, EventArgs e) { button1.Click -= button1_Click; this.KeyDown -= Form1_KeyDown; i++; Thread.Sleep(2000); button1.Text = i.ToString(); Application.DoEvents(); button1.Click += button1_Click; this.KeyDown += Form1_KeyDown; } private void Form1_KeyDown(object sender, KeyEventArgs e) { button1.Click -= button1_Click; this.KeyDown -= Form1_KeyDown; i++; Thread.Sleep(2000); button1.Text = i.ToString(); Application.DoEvents(); button1.Click += button1_Click; this.KeyDown += Form1_KeyDown; }关键是 Application.DoEvents()要加 ,并且加的位置一定在加事件之前,不知道什么原因我有加了一个Keydown事件,因为我也不想别的控件,或者快捷键有效,这样写就是累一点所有控件都要这样写 WINFORM在按钮事件没结束之前是不可以重复按的吧?出现卡着不动到是可能的,例如执行大数据的查询或者启动某个硬件设备,但能反复按没听说过只有的B/S架构中出现网络延迟才会出现这样的问题 我希望卡住,但是没卡住,UI卡住了,动作可没卡住,俺以前是搞Delphi的就没有这个情况 你这个绝对有何根据?不懂不要来乱说好吧,这个取消事件是这个阻止事件的最好方法,比改变Enabled属性要处理的操作少的多。一个是直接的事件取消,一步到位(唯一要注意的是事件的重新加载),另一个却是先改变属性值,然后发送界面刷新消息通知Windows要刷新界面重绘,而且不用Application.DoEvent(),就会阻塞这个重绘过程而优先执行自己写的后续代码。 原来如此,但你那个一步到位的代码还是必须要Application.DoEvent() 我什么时候说过要在-=操作后调用Application.DoEvent()的?你看过#12楼的代码没有,楼主都说可以用了,而后面提到的那个是针对Enable改变是增加的,界面不重绘,根本不需要Application.DoEvent()。另外这个-=取消事件还有一个非常重要的使用场合,就是在事件执行中,例如ValueChenged等值改变事件中,手动修改了值,会引发事件无限被执行的情况,这时只要取消事件后,那么里面执行的任何操作都将不会有问题。还有验证输入是否有误的时候也是,如果有误,不允许焦点离开该控件,但是如果事件中不先取消它本身的执行,也可能会导致无限被触发验证。 你真的没说过-=后要调用Application.DoEvent(),而且8楼一步到位的代码还特意去掉了Application.DoEvent()然后我不但看了三遍12楼的代码,两份代码里确实都有Application.DoEvent()还亲自测试了,得出的结论就是:你的一步到位的代码,也和Enabled=false一样需要Application.DoEvent()才能一步到位 被你这么一说,的确他加了Application.DoEvents();,因为这行代码一般都紧跟着“阻止代码”如Enable改变或-=取消事件之后的,所以我只看了前面两行代码,没看下去。这说明楼主根本没搞懂Application.DoEvents();的作用,滥用它,他那个地方根本不需要添加的,建议你自己实际编写代码测试下,我所说的自己写过很多代码在实际项目中了,肯定没错的。 我是没有搞懂Application.DoEvents()的用法,但是不用,还是不好用,所以我就加了。真正原因谁能指导一下 谁能详细的解释下Application.DoEvents()用法? 我特意模拟了下按钮的连点(实际谁吃饱了没事去连点啊)。发现这个事件有个排队等待的过程,也就是说,当设置了Enable为false后,立刻禁止按钮的点击,但是这个过程还是没你连点速度快的话,还是会至少执行了2次事件,而你在事件执行后(延迟2秒后)再执行Application.DoEvents(),意味着提前响应那个鼠标点击事件(如果不调用则会在全部执行后响应),这个响应时间点很重要,因为在Application.DoEvents()插入点响应的话,由于此刻已经取消了事件处理函数,所以将无任何动作,你不添加Application.DoEvents(),变成了执行结束后才响应点击事件,这个时候事件又被重新加载了。如果你对执行次数有严格控制,不允许执行2次(一般2次以上执行是没啥关系的),最好判断下那个变量i的值,代码如下: private void button1_Click(object sender, EventArgs e) { if (i == 0) { i++; Thread.Sleep(2000); button1.Text = i.ToString(); } }当然你设置bool类型的变量用于判断也足够。我感觉这样会更好些,不然即使用户在你点击按钮执行过程中没有多次点击,按钮执行过程结束后再去点击呢?你还得控制的话,肯定要用变量来记录是否点击过了。 MSDN上有Application.DoEvents()的详细介绍,这里是用来处理WINDOWS消息响应的,在Application.DoEvents()的插入点,所有排队等待的WINDOWS消息都会依次响应(例如改变控件属性要体现在界面上,点击按钮要触发按钮的点击事件等)后,再执行后续代码。 Application.DoEvent()最好不要使用,要实现你说的效果可以试一下win32 api peekmessage把消息队列中的鼠标点击事件去掉 意思用17楼代码已经可以了吗?如果还不行,试试下面三个函数呢 this.SuspendLayout(); this.ResumeLayout(false); this.PerformLayout();这三个函数或许对你有帮助。申明:我没有测试过 记事本按行编辑与删除 C#如何判断默认的打印机否是tsc打印机?????? .Net 奇怪的问题 一个表50个字段正常吗? 急用谢谢帮忙 listbox控件选中时更改显示背景和字体颜色 关于ADO.NET机制的一个问题。 load事件为什么会执行两次 调用WCF服务并传递参数 怎样把WAV或MP3格式的文件转换成VOC格式的?---在线等!!!! C#是否可以执行数据库里的Job 可以用streamreader method来做把excel导入datagridview嘛
private void button1_Click(object sender, EventArgs e)
{
if(!IsPress)
{
IsPress = true;
i++;
Thread.Sleep(2000);
button1.Text = i.ToString();
IsPress = false;
}
}
button1.Enabled = true;说说为什么不好用
private void button1_Click(object sender, EventArgs e)
{
if(button1.Enabled)
{
button1.Enabled = false;
i++;
Thread.Sleep(2000);
button1.Text = i.ToString();
button1.Enabled = true;
}
}
这样做肯定没问题,不过最好的做法是取消事件,写法如下:
private void button1_Click(object sender, EventArgs e)
{
button1.Click -= button1_Click;
i++;
Thread.Sleep(2000);
button1.Text = i.ToString();
//注意,如果代码执行过程可能有异常发生,
//必须用try-finally语句,
//并将此行代码加入到finally中执行
button1.Click += button1_Click;
}
但按楼主的要求,不是很赞成使用
-= +=内部要处理的操作绝对比Enabled属性,或者标志量要多
还是使用Enabled属性或者标志量来处理吧
下面从产品方面来说:
按钮不给点,就应该设置成Enabled = false
如果Enabled = true,用户点了又没反应,难免会有用户抱怨了:
Y的,什么意思啊,Enabled = true,点了又没反应,是不是bug啊
所以要解决这个问题,要么在事件处理结束前把Windows消息队列中按钮的单击消息清除掉,要么用多线程去处理事务,让UI线程立即返回,以响应下一个Click消息,这时就可以通过标志量或者事件去控制按钮是否响应事件了。第一种方案没研究过,第二种就简单多了。private void button1_Click(object sender, EventArgs e)
{
if (button1.Enabled)
{
button1.Enabled = false;
Thread t = new Thread(() =>
{
i++;
Thread.Sleep(2000);
button1.Invoke(new Action(() =>
{
button1.Text = i.ToString();
button1.Enabled = true;
}));
});
t.IsBackground = true;
t.Start();
}
}
{
button1.Click -= button1_Click;
this.KeyDown -= Form1_KeyDown;
i++;
Thread.Sleep(2000);
button1.Text = i.ToString();
Application.DoEvents();
button1.Click += button1_Click;
this.KeyDown += Form1_KeyDown;
} private void Form1_KeyDown(object sender, KeyEventArgs e)
{
button1.Click -= button1_Click;
this.KeyDown -= Form1_KeyDown;
i++;
Thread.Sleep(2000);
button1.Text = i.ToString();
Application.DoEvents();
button1.Click += button1_Click;
this.KeyDown += Form1_KeyDown;
}关键是 Application.DoEvents()要加 ,并且加的位置一定在加事件之前,不知道什么原因
我有加了一个Keydown事件,因为我也不想别的控件,或者快捷键有效,这样写就是累一点所有控件都要这样写
出现卡着不动到是可能的,例如执行大数据的查询或者启动某个硬件设备,但能反复按没听说过
只有的B/S架构中出现网络延迟才会出现这样的问题
原来如此,但你那个一步到位的代码还是必须要Application.DoEvent()
你真的没说过-=后要调用Application.DoEvent(),而且8楼一步到位的代码还特意去掉了Application.DoEvent()
然后
我不但看了三遍12楼的代码,两份代码里确实都有Application.DoEvent()
还亲自测试了,得出的结论就是:
你的一步到位的代码,也和Enabled=false一样需要Application.DoEvent()才能一步到位
发现这个事件有个排队等待的过程,也就是说,当设置了Enable为false后,立刻禁止按钮的点击,但是这个过程还是没你连点速度快的话,还是会至少执行了2次事件,而你在事件执行后(延迟2秒后)再执行Application.DoEvents(),意味着提前响应那个鼠标点击事件(如果不调用则会在全部执行后响应),这个响应时间点很重要,因为在Application.DoEvents()插入点响应的话,由于此刻已经取消了事件处理函数,所以将无任何动作,你不添加Application.DoEvents(),变成了执行结束后才响应点击事件,这个时候事件又被重新加载了。
如果你对执行次数有严格控制,不允许执行2次(一般2次以上执行是没啥关系的),最好判断下那个变量i的值,代码如下:
private void button1_Click(object sender, EventArgs e)
{
if (i == 0)
{
i++;
Thread.Sleep(2000);
button1.Text = i.ToString();
}
}
当然你设置bool类型的变量用于判断也足够。
我感觉这样会更好些,不然即使用户在你点击按钮执行过程中没有多次点击,按钮执行过程结束后再去点击呢?你还得控制的话,肯定要用变量来记录是否点击过了。
如果还不行,试试下面三个函数呢
this.SuspendLayout();
this.ResumeLayout(false);
this.PerformLayout();
这三个函数或许对你有帮助。
申明:我没有测试过