可能重复:
How to block Winforms UI while background thread is running
我正在使用C#WinForm应用程序
我在屏幕上有一个保存按钮,屏幕数据保存到数据库中。
当用户点击按钮应用程序进入数据库并保存数据时会发生什么。 这需要一些时间。
但是,如果用户再次单击“保存”按钮,则会触及Click事件,当第一次单击“返回主代码”(保存数据库后)时,捕获的事件将被触发..
简而言之,当线程从第一个事件返回时,会捕获并触发click事件 (我尝试了启用/禁用按钮的方案。)
如何阻止此行为。
此致 AKHIL
@Jalal:我尝试使用
进行了一些修改private readonly object _userActivityLocker = new object();
private void btnSave_Click(object sender, EventArgs e)
{
if (System.Threading.Monitor.TryEnter(_userActivityLocker))
{
//note that any sub clicks will be ignored while we are here
try
{
DateTime dt = DateTime.Now;
Thread.Sleep(2000);
Debug.Print("FirstClick {0} Second Click {1}",dt.ToLongTimeString(), DateTime.Now.ToLongTimeString());
//here it is safe to call the save and you can disable the btn
Application.DoEvents();
}
finally
{
System.Threading.Monitor.Exit(_userActivityLocker);
//re-enable the btn if you disable it.
}
}
}
但是当我快速点击按钮(我用5次快速点击检查)时,点击5次活动 已被解雇并且控制台窗口正在显示
FirstClick 1:30:22 PM第二次点击1:30:24 PM FirstClick 1:30:24 PM第二次点击1:30:26 PM FirstClick 1:30:26 PM第二次点击1:30:28 PM FirstClick 1:30:28 PM第二次点击1:30:30 PM FirstClick 1:30:30 PM第二次点击1:30:32 PM
答案 0 :(得分:5)
问题在于,当您将数据保存到数据库时,您的程序已经死了。用户的鼠标单击位于消息队列中,等待UI线程恢复生命。如果是,则不再禁用该按钮,以便触发Click事件。
您可以在重新启用按钮之前清空消息队列来解决此问题,以便在按钮被禁用时处理单击:
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
// Save data to database
//...
System.Threading.Thread.Sleep(2000);
Application.DoEvents(); // Empty the message queue
if (!button1.IsDisposed) button1.Enabled = true;
}
不要跳过IsDisposed测试,DoEvents很危险,因为它没有选择处理哪些事件。当您的代码仍在运行时,它将很乐意让用户关闭主窗口。
但更好的解决方案是不要让你的UI线程像这样死掉。使用BackgroundWorker在工作线程上执行保存。这也将避免当你的保存需要超过几秒时,Windows提出的丑陋的“无响应”鬼窗口。它现在可能还没有这样做,但是从现在开始dbase成长的那一年。您可以在BGW的RunWorkerCompleted事件处理程序中重新启用该按钮。
答案 1 :(得分:1)
启用,然后在您避开时再次重新启用。这有什么问题?
public void SaveButton_Click(..., ...)
{
this.SaveButton.Enabled = false;
Save();
this.SaveButton.Enabled = true;
}
答案 2 :(得分:1)
使用System.Threading.Monitor
类可以实现:
private readonly object _userActivityLocker = new object();
private void btnSave_Click(object sender, EventArgs e)
{
new Thread(delegate()
{
if (System.Threading.Monitor.TryEnter(_userActivityLocker))
{
//note that any sub clicks will be ignored while we are here
try
{
//here it is safe to call the save and you can disable the btn
}
finally
{
System.Threading.Monitor.Exit(_userActivityLocker);
//re-enable the btn if you disable it.
}
}
}) { IsBackground = true }.Start();
}
要证明更改按钮以启用或禁用状态是不够的,这是一个简单的测试:
添加一个新表单并向其添加一个button1,在button1 click事件处理程序中写下以下代码:
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
Console.WriteLine("First Message");
Thread.Sleep(2000);
Console.WriteLine("second Message");
button1.Enabled = true;
}
然后构建并运行应用程序,双击button1,输出窗口中的结果将是:
First Message
second Message
First Message
second Message
所以我们必须确保即使双击左右也只执行一次点击,只需使用System.Threading.Monitor
更新:请注意,您可以使用“如果是C#4.0”任务,ThreadPool.QueueUserWorkItem或BackgroundWorker作为Thread的替代。