在一个事件没结束之前,Click事件再不响应

解决方案 »

  1.   

     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;
     }  
    }
      

  2.   

    button1.Enabled = false;
    button1.Enabled = true;说说为什么不好用
      

  3.   

    也可以这么用吧
    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;
      }
     }
      

  4.   

    其实你那个是因为界面没来得及响应那个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;
    }
      

  5.   

    虽然-= += 很优雅
    但按楼主的要求,不是很赞成使用
    -= +=内部要处理的操作绝对比Enabled属性,或者标志量要多
    还是使用Enabled属性或者标志量来处理吧
      

  6.   

    上面是从技术方面来说
    下面从产品方面来说:
    按钮不给点,就应该设置成Enabled = false
    如果Enabled = true,用户点了又没反应,难免会有用户抱怨了:
    Y的,什么意思啊,Enabled = true,点了又没反应,是不是bug啊
      

  7.   

    这个问题的关键在于,由于是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();
    }
    }
      

  8.   

    有没有什么设定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事件,因为我也不想别的控件,或者快捷键有效,这样写就是累一点所有控件都要这样写
      

  9.   

    WINFORM在按钮事件没结束之前是不可以重复按的吧?
    出现卡着不动到是可能的,例如执行大数据的查询或者启动某个硬件设备,但能反复按没听说过
    只有的B/S架构中出现网络延迟才会出现这样的问题
      

  10.   

    我希望卡住,但是没卡住,UI卡住了,动作可没卡住,俺以前是搞Delphi的就没有这个情况
      

  11.   

    你这个绝对有何根据?不懂不要来乱说好吧,这个取消事件是这个阻止事件的最好方法,比改变Enabled属性要处理的操作少的多。一个是直接的事件取消,一步到位(唯一要注意的是事件的重新加载),另一个却是先改变属性值,然后发送界面刷新消息通知Windows要刷新界面重绘,而且不用Application.DoEvent(),就会阻塞这个重绘过程而优先执行自己写的后续代码。
      

  12.   


    原来如此,但你那个一步到位的代码还是必须要Application.DoEvent()
      

  13.   

    我什么时候说过要在-=操作后调用Application.DoEvent()的?你看过#12楼的代码没有,楼主都说可以用了,而后面提到的那个是针对Enable改变是增加的,界面不重绘,根本不需要Application.DoEvent()。另外这个-=取消事件还有一个非常重要的使用场合,就是在事件执行中,例如ValueChenged等值改变事件中,手动修改了值,会引发事件无限被执行的情况,这时只要取消事件后,那么里面执行的任何操作都将不会有问题。还有验证输入是否有误的时候也是,如果有误,不允许焦点离开该控件,但是如果事件中不先取消它本身的执行,也可能会导致无限被触发验证。
      

  14.   


    你真的没说过-=后要调用Application.DoEvent(),而且8楼一步到位的代码还特意去掉了Application.DoEvent()
    然后
    我不但看了三遍12楼的代码,两份代码里确实都有Application.DoEvent()
    还亲自测试了,得出的结论就是:
    你的一步到位的代码,也和Enabled=false一样需要Application.DoEvent()才能一步到位
      

  15.   

    被你这么一说,的确他加了Application.DoEvents();,因为这行代码一般都紧跟着“阻止代码”如Enable改变或-=取消事件之后的,所以我只看了前面两行代码,没看下去。这说明楼主根本没搞懂Application.DoEvents();的作用,滥用它,他那个地方根本不需要添加的,建议你自己实际编写代码测试下,我所说的自己写过很多代码在实际项目中了,肯定没错的。
      

  16.   

    我是没有搞懂Application.DoEvents()的用法,但是不用,还是不好用,所以我就加了。真正原因谁能指导一下
      

  17.   

    谁能详细的解释下Application.DoEvents()用法?
      

  18.   

    我特意模拟了下按钮的连点(实际谁吃饱了没事去连点啊)。
    发现这个事件有个排队等待的过程,也就是说,当设置了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类型的变量用于判断也足够。
    我感觉这样会更好些,不然即使用户在你点击按钮执行过程中没有多次点击,按钮执行过程结束后再去点击呢?你还得控制的话,肯定要用变量来记录是否点击过了。
      

  19.   

    MSDN上有Application.DoEvents()的详细介绍,这里是用来处理WINDOWS消息响应的,在Application.DoEvents()的插入点,所有排队等待的WINDOWS消息都会依次响应(例如改变控件属性要体现在界面上,点击按钮要触发按钮的点击事件等)后,再执行后续代码。
      

  20.   

    Application.DoEvent()最好不要使用,要实现你说的效果可以试一下win32 api peekmessage把消息队列中的鼠标点击事件去掉
      

  21.   

    意思用17楼代码已经可以了吗?
    如果还不行,试试下面三个函数呢
      this.SuspendLayout();
      this.ResumeLayout(false);
                this.PerformLayout();
    这三个函数或许对你有帮助。
    申明:我没有测试过