如何在C#Winform中锁定应用程序GUI

可能重复:
如何在后台线程运行时阻止Winforms UI

我正在使用C#WinForm应用程序

我在屏幕上有一个Save按钮,屏幕数据保存到数据库中。

当用户点击按钮应用程序进入数据库并保存数据时会发生什么。 这需要一些时间。

但是,如果用户再次单击“保存”按钮,则会触发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

问题是你的程序在将数据保存到数据库时对世界是死的。 用户的鼠标单击位于消息队列中,等待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事件处理程序中重新启用该按钮。

通过启用,然后在您避开时再次重新启用。 这有什么问题?

 public void SaveButton_Click(..., ...) { this.SaveButton.Enabled = false; Save(); this.SaveButton.Enabled = true; } 

使用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(); } 

要certificate更改按钮以启用或禁用状态是不够的,这里是一个简单的测试:

添加一个新表单并向其添加一个button1,并在button1单击事件处理程序内部编写以下代码:

 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的替代。