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控件绑定到代码。所以我们不能在这种情况下运行后台线程。 帮我解决问题。
答案 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;
}