禁用按钮在WPF中触发事件

时间:2012-03-02 03:03:12

标签: c# wpf events

private void but_Click(object sender, RoutedEventArgs e)
{
    (sender as Button).IsEnabled = false;
    doSomeThing();//e.g run for more than 20 seconds
    (sender as Button).IsEnabled = true;
}

第一次按下按钮时,它会禁用。然后启动doSomeThing(),它包含UI代码或更新的一些UI变量。 我的意思是,如果我在doSomeThing()正在进行时再次按下该按钮,则此按钮启用后会再次触发but_Click事件。 它保持事件队列被触发,即。我压了多次。

那么,如何在禁用按钮时阻止触发事件? 请考虑在这种情况下'doSomething'包含UI控件绑定到代码。所以我们不能在这种情况下运行后台线程。 帮我解决问题。

5 个答案:

答案 0 :(得分:4)

似乎有些澄清...... [/ p>

  • 已正确禁用控件(例如按钮)。
  • 只要主线程正在处理原始点击事件,就不会处理后续事件 - 相反,它们会排队。
  • 首次点击事件完成后,将处理下一个排队事件。
  • 此时,由于控件已经启用,排队的点击事件将再次触发事件处理程序。

有几种可能的解决方案,我认为@Amit是在正确的轨道上 - 使用线程可能是最好的方法。

以下是对我有用的简化版本:

private void Button1_Click(object sender, EventArgs e)
{
    disableControls(); // e.g.- Button1.Enabled = false;

    // run "doSomething" in a separate thread
    new Thread(new ThreadStart(doSomething)).Start();
}

private void doSomething()
{
    // do something... make sure it's thread-safe!!
    // ...

    enableControls();  // a thread safe enabling of relevant controls
}

为了更好地解释为什么不使用Application.DoEvents()look here

对于控件状态的线程安全操作,请参阅: How to: Make Thread-Safe Calls to Windows Forms Controls

答案 1 :(得分:1)

此代码的问题是在{i}}方法中运行doSomeThing()方法。因此,Button未正确禁用。如果我们重构代码以便doSomeThing()方法在不同的线程中运行,那么它将正常工作。这是一个使用BackgroundWorker的简单示例;但是这个想法是我们不应该在UI线程中运行耗时的东西。这是重构的代码:

public partial class ButtonEnableTest : Window
{
    private BackgroundWorker worker = new BackgroundWorker();

    public ButtonEnableTest()
    {
        InitializeComponent();
        this.worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (this.btn.IsEnabled == false)
        {
            this.btn.IsEnabled = true;
        }
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        doSomeThing();
    }

    private void doSomeThing()
    {
        int i = 5;
        while ( i > 0)
        {
            Thread.Sleep(TimeSpan.FromMilliseconds(2000));
            System.Diagnostics.Debug.WriteLine("Woke up " + i);
            i--;
        }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Button btn = (Button) sender;
        System.Diagnostics.Debug.WriteLine("at ButtonClick");
        if (btn.IsEnabled)
        {
            btn.IsEnabled = false;
            this.worker.RunWorkerAsync();
        }
    }
}

我没有在这里进行任何编码转换,因为我只是想分享我的想法。请注意,我将WPF按钮命名为“btn”。

答案 2 :(得分:1)

另一种方法是使用PeekMessage API函数,如以下代码所示。

using System.Runtime.InteropServices;
using System.Security;

[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
    public IntPtr handle;
    public uint msg;
    public IntPtr wParam;
    public IntPtr lParam;
    public uint time;
    public System.Drawing.Point p;
}

[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool PeekMessage(out NativeMessage message,
    IntPtr handle, uint filterMin, uint filterMax, uint flags);
private const UInt32 WM_MOUSEFIRST = 0x0200;
private const UInt32 WM_MOUSELAST = 0x020D;
public const int PM_REMOVE = 0x0001;

// Flush all pending mouse events.
private void FlushMouseMessages()
{
    NativeMessage msg;
    // Repeat until PeekMessage returns false.
    while (PeekMessage(out msg, IntPtr.Zero,
        WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
        ;
}

此代码包含一系列API函数及其参数的声明。 (您还需要为System.Runtime.InteropServices和System.Security名称空间添加using语句。下载示例以获取详细信息。)

FlushMouseMessages方法调用PeekMessage,告诉它放弃WM_MOUSELAST到PM_REMOVE范围内的任何消息。代码重复调用PeekMessage,直到它返回false表示没有这样的消息。

以下按钮事件处理程序调用FlushMouseMessage,因此在代码仍在运行时无法单击该按钮。

private void but_Click(object sender, RoutedEventArgs e)
{
    (sender as Button).IsEnabled = false;
    doSomeThing();//e.g run for more than 20 seconds
    (sender as Button).IsEnabled = true;
    FlushMouseMessages();
}

我从网站上选择了上面的代码 http://csharphelper.com/blog/2015/08/flush-click-events-in-c/ 这对我有用。

答案 3 :(得分:0)

private void but_Click(object sender, RoutedEventArgs e)
{
    Button but = (sender as Button)
    if(but.IsEnabled)
    {
       but.IsEnabled = false;
       doSomeThing();
       but.IsEnabled = true;
    }
}

答案 4 :(得分:0)

我遇到了与TextChanged事件类似的问题,并使用该代码解决了这个问题:

private void TB_box_TextChanged(object sender, TextChangedEventArgs e)
{
   //detach event
   TB_box1.TextChanged -= TB_box_TextChanged;
   TB_box2.TextChanged -= TB_box_TextChanged;

   //change my value
   TB_box1.Text = "a"
   TB_box2.Text = "b"

   //attach event     
   TB_box1.TextChanged += TB_box_TextChanged;
   TB_box2.TextChanged += TB_box_TextChanged;

}