我有一个显示WinForms表单的控制台应用程序。
在表单中,用户单击按钮1 并运行一个长子例程。我希望有一个按钮2 可以在任何时候杀死子程序。但是,当我单击按钮1 直到子例程完成时,UI会冻结。如何让UI不冻结?
答案 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组件