如何在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

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

问题是,当您的程序将数据保存到数据库时,它已经死了。用户的鼠标单击位于消息队列中,等待您的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。