在C#中执行任何其他操作时表单无响应

时间:2011-02-07 07:51:08

标签: c# winforms

我有一个表单(使用VS2010在C#中开发)和一个进度条。 这是一种秒表形式,我填写进度条说10secs ....随着时间流逝,进度条相应填充....意味着5secs后,进度条将填充50%等等....

我使用for循环来执行此操作: -

for(int count=0;count<200;count++)
{
   progbar.performstep();
   progbar.update();
   update();
   Thread.Sleep(50);
}

我使用了50毫秒的Thread.Sleep,以便进度条顺利更新。 持续1秒,它会逐步递增。

问题是,如果我执行任何其他操作,例如移动此表单,甚至单击桌面上的其他图标,则会使此表单“无响应”....但它执行操作并在10秒结束时重新获得它的状态并显示弹出窗口,表示过程栏已满,已经过了10秒。

感谢您的帮助,并抱歉使用这种复杂的语言。

此致 Swanand

更新:我在下面的答案的帮助下解决了这个问题....我要知道的一个常见错误就是忘记了“Applications.DoEvents()”以及“update() “....如果你进入这条线,被”绞死“的机会就会减少!

5 个答案:

答案 0 :(得分:14)

您正在UI线程上执行长时间运行的操作,这意味着UI“消息循环”(负责处理用户输入和更新屏幕等事件)无法运行。< / p>

您应该在不同的线程上执行长时间运行的操作(无论您是自己创建的还是后台线程),并使用BackgroundWorker轻松更新进度条,或使用Control.Invoke/BeginInvoke进行编组当您需要更新UI时,委托回调UI线程。 (您不能从错误的线程更新控件。)

如果您的唯一用户界面互动正在填写进度条,我建议您使用BackgroundWorker

如果你没有真正做“真正的”工作,只是等待时间过去,你可以使用System.Windows.Forms.Timer而不是所有这些。这将在UI线程上“勾选”,但不会阻止滴答之间的UI线程。如果你没有很多工作要做,你应该只使用它 - 如果真的只是更新进度条,而不是(比如说)处理文件等。注意你不应该' t依靠定时器准确“准时”发射 - 您应该根据观察到的时间而不是观察到的刻度数来设置进度条的位置。

答案 1 :(得分:7)

您正在阻止UI线程,这意味着它不处理诸如“paint”之类的事件。要正确执行此操作,您应该使用BackgroundWorker之类的内容,只需从progress事件更新UI。

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MyForm());
    }
}

class MyForm : Form
{
    Button btn;
    BackgroundWorker worker;
    ProgressBar bar;
    public MyForm()
    {
        Controls.Add(btn = new Button { Text = "Click me" });
        btn.Click += new EventHandler(btn_Click);

        Controls.Add(bar = new ProgressBar { Dock = DockStyle.Bottom, Visible = false, Minimum = 0, Maximum = 100 });

        worker = new BackgroundWorker { WorkerReportsProgress = true };
        worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        bar.Visible = false;
        if (e.Error != null)
        {
            Text = e.Error.Message;
        }
        else if (e.Cancelled)
        {
            Text = "cancelled";
        }
        else
        {
            Text = e.Result == null ? "complete" : e.Result.ToString();
        }
        btn.Enabled = true;
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int count = 0; count < 100; count++)
        {
            worker.ReportProgress(count);
            Thread.Sleep(50);
        }
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        bar.Value = e.ProgressPercentage;
    }

    void btn_Click(object sender, EventArgs e)
    {
        bar.Value = 0;
        bar.Visible = true;
        btn.Enabled = false;
        worker.RunWorkerAsync();
    }
}

答案 2 :(得分:2)

您正在阻止主UI线程。您可以使用后台工作程序执行此操作。您可以在MSDN

中找到更多详细信息

答案 3 :(得分:0)

Marc建议的答案会有所帮助。 Lon运行操作可能会使您的应用程序崩溃或无法响应。我有一篇与后台工作者类的用法相关的博客文章。

http://midnightprogrammer.net/post/Using-Background-Worker-in-C.aspx

答案 4 :(得分:0)

如果你想运行你的代码,你应该将这段代码放在一个函数中,并用一个线程调用这个函数。

 public static void fun1()
        {
            for (int i = 0; i <= 10; i++)
            {
                Console.Write("This is function1");
                Console.Write("\n");

            }

        }
  Thread firstthread = new Thread(new ThreadStart(fun1));

  firstthread.Start();
  firstthread.suspend();//whenever you want your current control to stop.

b'caz Thread.sleep(100)将停止整个上下文而不是你想要的那个..