【问题标题】:C# windows forms handle click spam [duplicate]C# windows窗体处理点击垃圾邮件[重复]
【发布时间】:2013-12-20 18:13:21
【问题描述】:

我有一个包含许多用户控件和每个按钮的应用程序,每个按钮都有一个 OnClick 事件,该事件执行一些重要的操作,然后发送到新的用户控件。

当用户多次点击非常快时,问题就出现了,事件代码在退出到新的用户控件之前执行了多次,从而导致问题。

我目前的解决方案是禁用事件处理程序第一行的按钮,但是对每个窗口和处理程序都这样做会很麻烦,我该怎么办?

编辑:从 Button 派生并覆盖 OnClick 事件是否是一个很好的解决方案,因此它总是检查“工作”变量,如果它是真的,它不会启动事件?比如:

public class MyButton : Button
{
private static bool isWorking = false;

protected override void OnClick(EventArgs e)
{
    if (!isWorking)
    {
        isWorking = true;
        base.OnClick(e);
        isWorking = false;
    }
    //Else do nothing
}

}

【问题讨论】:

    标签: c# winforms events click


    【解决方案1】:

    您可以使用一些timeStamp 来延迟两次点击:

    DateTime timeStamp;
    //this will handle the clicks with the allowed interval being 0.5 second
    //Note that a tick is equal to 1/10,000,000 of second.
    private void click_Handler(object sender, EventArgs e) {
       if ((DateTime.Now - timeStamp).Ticks < 5000000) return;
       timeStamp = DateTime.Now;
       //your code goes here ....
    }
    

    【讨论】:

    • 同上,我必须在每个事件处理程序上复制这段代码
    • @user1777914 不要这么懒惰,只有一种方法不必为控件添加某些事件的处理程序,就是创建自定义控件,处理内部的所有内容。我怀疑您甚至不知道一个处理程序可以处理许多事件,这意味着在这种情况下click_Handler 可以处理您按钮的所有Click 事件。你只需要检查sender就可以看到点击了哪个按钮,那么你为什么认为这里有任何重复的代码
    • 如果我可以在我的处理程序中添加一个动作过滤器属性(就像 ASP.NET MVC 属性),那就太好了,这样我就可以在之前和之后执行代码。但是 C# 不支持装饰器和拦截器 =/
    【解决方案2】:

    如果您希望所有按钮都等到一个按钮的工作完成,请在表单中添加一个bool isProcessing 变量。将每个按钮的工作包装在 if (!isProcessing) 中,并在 if 语句的第一行中将该标志设置为 true。然后不要忘记在退出 if 之前将其设置回 false。

    我假设您正在异步执行所有这些操作,因为如果它们都在同一个线程中,表单将在处理工作时锁定。不过,这将解决您的问题。

    【讨论】:

    • 同上,我必须在每个事件处理程序上复制相同的代码(如果!isProcessing,设置真/假),我想避免这种情况。
    • 不幸的是,没有一个简单的解决方案可以让所有事件处理程序都做某事而不必全部编码。
    【解决方案3】:

    在进行敏感操作时禁用控件是我一直采用的典型解决方案。

    但由于在一个屏幕上可能有相当多的控件会受到一些点击或 UI 更改的影响,所以我通常会设计一种专门的方法来遍历所有受影响的控件并相应地禁用/启用它们。

    类似这样的:

    void EnableControls(bool enable)
    {
        foreach (Control ctl in this.Controls)
            ctl.Enabled = enable;
    }
    

    同样,您可以将控件分组到相关的存储桶中,以便仅禁用/启用其中一个等。取决于您的确切需求。

    还有一种使用计时器的替代解决方案 - 禁用按钮,但在 1 秒后启用它。这可以防止紧张的用户多次单击会导致数据损坏(即每次单击都被视为新操作)。

    【讨论】:

    • 这里的问题是我必须将此方法(及其对每个事件处理程序的调用)添加到我的表单中的每个用户控件中,我不是在谈论按钮,而是在谈论改变和模拟的用户控件导航,我只想在整个代码中执行一次,而不是在每个用户控件和事件处理程序上复制相同的代码
    • 重点在于不要在任何地方复制代码。如果您能找到通用逻辑,那么您可以为所有控件设置一次处理程序 - 只需遍历所有控件并使用相同的处理程序订阅每个按钮的 Click 事件。该处理程序以相同的方式对表单上的所有按钮调用启用/禁用。
    • 有些东西我想你不明白我的意思,问题是按钮不多,但有很多用户控件(每个都模拟不同的窗口),每个控件都有 1 或 2 个按钮,这些按钮可以做敏感的工作
    【解决方案4】:

    我会从每个按钮调用相同的函数,然后执行特定任务:

    private void Button_Click(object sender, EventAgrs e)
    {
        Button btn = sender;
        btn.disable = true;
        switch (btn.AccessibleName)
        // call specific function for the particular button or do it all here
    }
    

    【讨论】:

    • 这是一种有趣的方法,但我必须重新编写每个事件处理程序,我认为这是不可能的。
    【解决方案5】:

    我不确定这是否可行,但只是一个想法......

    您可以尝试使用面向方面的方法(例如借助 Postsharp):

    1. 创建两个方面,一个用于方法入口,一个用于方法出口。在方法条目中,将当前方法标记为“处理”(例如,将方法名称添加到哈希集中)。在方法退出中,将该方法标记为“未处理”(将其从哈希集中删除)。然后在方法入口中检查方法是否正在处理,如果是,则取消该方法(如:https://stackoverflow.com/a/2437794/113858
    2. 使用此方面标记所有事件处理程序。

    【讨论】:

    • 是的,使用面向方面的方法会很好,我不知道为什么 C# 本身不支持这种方法,使用 ASP.NET MVC,你有动作过滤器,可以做你上面描述的几乎所有的事情,这应该可以在没有任何外部库的情况下实现。
    猜你喜欢
    • 2017-09-22
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 2015-05-18
    • 2019-07-19
    • 2014-04-14
    • 1970-01-01
    • 2013-12-31
    相关资源
    最近更新 更多