我看到了很多关于如何从不同线程编辑 C# 表单上的控件的问题,但对我来说没有多大意义。我知道您不能从主线程以外的其他线程更改任何 UI。为了使这项工作有效,您必须使用调用并从那里安全地编辑控件?
我有一个按钮开始写入文件,当您按下按钮时,按钮本身就会被禁用,因此您无法启动多个执行完全相同操作的线程。写入完成后,我希望按钮再次可用,但我无法让它在另一个线程上工作。
我将其作为表单中的Generate_Click 事件。
private void Generate_Click(object sender, EventArgs e)
{
Generate.Enabled = false;
int x = 512;
int y = 512;
MBrot mbrot = new MBrot(x, y);
PB_Update lb = new PB_Update(0, y, Generator_PB, Generate, mbrot, this);
lb.Start();
}
这是 PB_Update.cs 中的 ThreadWork() 函数,当 while 循环完成时,对文件的写入完成,线程也完成,因此它结束并给出一个带有“完成”的消息框,现在按钮需要再次启用。
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.Enabled = true;
}
对于 WinForms,您可以通过 Control.BeginInvoke 方法直接在创建控件的线程上执行,您也可以使用 Control.Invoke,但是,Control.BeginInvoke 更适合 UI 操作,因为它将异步执行。
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.BeginInvoke((Action)delegate()
{
Generate_.Enabled = true;
});
}
以某种方式获取对托管generate_按钮的表单的引用(我们称之为
myform
)。然后,在 ThreadWork 的底部:
myform.Invoke(new Action(() => {
myform.SetGenerateEnabled();
}));
然后在表单中创建适当启用按钮的方法。 (我使用了一种方法,而不是直接更新按钮,这样就不会公开暴露按钮。)
这会在myform的线程上执行
{ ... }
内的命令,这是一个UI线程,因为它是UI。至少,我是这么理解的。这就是我从其他线程更新所有 UI 的方式。
以下是启动异步任务的简单示例,该任务禁用按钮 5 秒,然后再次启用。同时,UI 的其余部分也可以正常工作。
请注意,此
async
方法与您的 Generate_Click
事件存在于同一类中,并在 UI 线程上运行。这意味着它可以启用和禁用该按钮。但长时间运行的任务在单独的线程上执行,因此它不会锁定 UI。
希望此示例为您提供修改自己的代码的基础:
private void Generate_Click(object sender, EventArgs e)
{
DisableButton(sender as Button, 5);
}
private async void DisableButton(Button sender, int secondsToDisable)
{
sender.Enabled = false;
// In your code, you would kick off your long-running process here as a task
await Task.Run(()=>Thread.Sleep(TimeSpan.FromSeconds(secondsToDisable)));
sender.Enabled = true;
}