如何使用Button在表单中停止长时间运行的子例程?

时间:2015-02-06 00:49:49

标签: c# winforms console-application

我有一个显示WinForms表单的控制台应用程序。

在表单中,用户单击按钮1 并运行一个长子例程。我希望有一个按钮2 可以在任何时候杀死子程序。但是,当我单击按钮1 直到子例程完成时,UI会冻结。如何让UI不冻结?

3 个答案:

答案 0 :(得分:2)

您长时间运行的代码阻止了UI线程,因此您不能再单击第二个按钮,也不能以任何方式与UI交互,直到代码执行完毕。

您需要将长时间运行的代码移动到单独的线程中。有各种(和更新的)方法,但有一种方法是BackgroundWorker。它非常容易学习,并且包含一些很好的功能,比如取消线程。

这是一个简短的WinForms应用程序来演示。您必须明确启用线程取消的功能。在此示例中,while循环无限期地继续,但每100ms检查一次,以查看是否有取消它的请求。单击第二个按钮时,将发送取消请求,并且线程结束。

public partial class Form1 : Form
{
    private BackgroundWorker bg;

    public Form1()
    {
        InitializeComponent();

        bg = new BackgroundWorker
             {
                 WorkerSupportsCancellation = true
             };

        bg.DoWork += (sender, args) =>
        {
            while (true)
            {
                Thread.Sleep(100);
                if (bg.CancellationPending)
                    break;
            }

            MessageBox.Show("Done!");
        };
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bg.RunWorkerAsync();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        bg.CancelAsync();
    }
}

关注chouaib的评论,在WinForms环境中使用BackgroundWorker的另一个好处是你可以将它放到你的设计器上,类似于菜单,计时器等。然后你可以访问它"属性中的成员"小组,设置" WorkerSupportsCancellation"到true,订阅活动等等。


来自你的评论:

  

"有没有办法运行此后台进程并能够更新主用户表单?我继续得到"跨线程操作无效的控件,从...以外的线程访问..."我想运行长时间运行的后台操作,并让它使用标签中的文本更新主UI(比如其进度的百分比)"

如果要在线程运行时更新UI,则应该从ProgressChanged事件执行此操作。首先,启用该选项并订阅事件:

bg.WorkerReportsProgress = true;

bg.ProgressChanged += bg_ProgressChanged;

当您想要更新UI时,请调用ReportProgress()。您可以传回一个完整百分比和一些文本,例如:

bg.ReportProgress(50, "Almost there...");

最后,从事件内部更新UI:

void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var message = e.UserState.ToString();
    var percent = e.ProgressPercentage;

    lblStatus.Text = message + " " + percent;
}

答案 1 :(得分:-1)

您需要按照评论中的建议将其设置为多线程。较旧的方法是管理自己的线程。然后是后台工作人员(便宜又简单)。现在,您可以使用其他选项,例如任务库。

记住 - UI线程上的任何运行都会阻止UI在该操作完成之前发送和接收事件。

答案 2 :(得分:-2)

查看BackgroundWorker组件