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

时间:2011-06-21 10:09:35

标签: c# winforms multithreading

  

可能重复:
  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

3 个答案:

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