在任务中更新UI控件

Vla*_*lad 1 c# asynchronous task winforms async-await

如果我在这样的任务中更新我的UI控件,它是否正确代码?
或者它是错的,我需要像Control.Invoke一样使用smth?

private async void button1_Click(object sender, EventArgs e)
{
    textBox1.Text = await Task<string>.Factory.StartNew(() =>
    {
        Foo();
        return "Completed";
    });
}

private void Foo()
{
    for (int i = 0; i < 100; i++)
    {
        textBox1.Text = i.ToString();
        Thread.Sleep(1000);
    }
}
Run Code Online (Sandbox Code Playgroud)

Pan*_*vos 7

BCL使用在Progress类中实现的IProgress接口来解决此特定方案,以提供丰富的异步进度报告.这在.NET 4.5或带有BCL Portability Nuget包的.NET 4中可用.许多BCL类接受用于进度报告的IProgress参数.

Servy的答案解决了在异步操作之后如何更新UI的直接问题,但这迫使您在长时间运行的操作中混合UI代码.IProgress允许您使用报表数据进行OnReport调用,而无需担心将调用编组到正确的线程,同步上下文,UI特定的调用等.

您的代码可以像这样简单:

private async void button1_Click(object sender, EventArgs e)
{
    var progress=new Progress<string>(msg=>textBox1.Text = msg);
    await Task<string>.Factory.StartNew(() => Foo(progress));
}

private void Foo(IProgress<string> progress)
{
    for (int i = 0; i < 100; i++)
    {
        progress.OnReport(i.ToString());
        Thread.Sleep(1000);
    }
    progress.OnReport("Finished");
}
Run Code Online (Sandbox Code Playgroud)

或者您可以使用更复杂的进度类型,例如

class MyProgressData
{
    public string Message{get;set;}
    public int Iteration {get;set;}
    public MyProgressData(string message,int iteration) ...
}

private async void button1_Click(object sender, EventArgs e)
{
    var progress=new Progress<MyProgressData>(msg=>{
        textBox1.Text = msg.Message;
        textBox2.Text=msg.Iteration.ToString();
    });
    await Task<string>.Factory.StartNew(() => Foo(progress));
}

private void Foo(IProgress<MyProgressData> progress)
{
    for (int i = 0; i < 100; i++)
    {
        progress.OnReport(new MyProgressData("Hi",i));
        Thread.Sleep(1000);
    }
    progress.OnReport("Finished");
}
Run Code Online (Sandbox Code Playgroud)

这样做的好处是,您可以完全将处理与报告分离.您可以将处理代码放在完全不同的类中,甚至可以从UI中进行投影.