如何在C#Winform中锁定应用程序GUI
本文关键字:锁定 应用程序 GUI C#Winform | 更新日期: 2023-09-27 17:59:40
可能重复:
如何在后台线程运行时阻止Winforms UI
我正在使用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次点击事件已启动,控制台窗口显示
第一次点击下午1:30:22第二次点击下午11:30:24第一次点击下午1:30:24第二次点击下午11:30:26第一次点击下午1:30:26第二次点击下午11:30:28第一次点击下午1:30:28第二次点击下午11:30:30第一次点击下午1:30:30第二次点击下午11:30:32
问题是,当您的程序将数据保存到数据库时,它已经死了。用户的鼠标单击位于消息队列中,等待您的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();
}
为了证明将按钮更改为启用或禁用状态是不够的,这里有一个简单的测试:
添加一个新表单并添加一个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;
}
然后构建并运行应用程序,双击按钮1,输出窗口中的结果将是:
First Message
second Message
First Message
second Message
因此,我们必须确保即使双击左右也只执行一次点击,而这只需使用System.Threading.Monitor
即可实现
更新:请注意,您可以使用Task"if C#4.0"、ThreadPool.QueueUserWorkItem或BackgroundWorker来代替Thread。