如何在长时间运行的功能中更新UI(文本字段)?

时间:2013-06-12 15:31:33

标签: c# winforms .net-4.0

我知道这个问题可能没有意义,而且我正在努力想办法解释它,所以我会展示一段代码来帮助。我在visual studio express 2010上使用Winforms:

private void button1(object sender, EventArgs e)
    {
        txtOutput.Text += "Auto-collecting variables. This may take several minutes";
        string v = foo();
        txtOutput.Text += "\n" + v;
        string b = bar();
        txtOutput.Text += "\n" + b;

        txtOutput.SelectionStart = txtOutput.Text.Length;
        txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
    }

所以基本上,当用户点击button1时,我希望“自动收集变量...”显示在文本框中,然后让foo()执行,显示它,然后执行bar(),然后显示。

当前正在发生的是foo()和bar()执行,然后在执行foo()和bar()之后一切都显示(需要几分钟的函数)。无论如何要解决这个问题,还是有解决方法?

编辑:C#的版本是4.0。如果我更新到4.5或5.0,没有.NET 4.5 / 5.0的计算机是否能够运行.exe?

5 个答案:

答案 0 :(得分:6)

C#5.0让这件事变得微不足道。

使用Task.Run在后​​台线程中执行长时间运行的任务,并使用await作为UI线程中的延续执行方法的其余部分,而不会在异步期间阻止UI线程任务。

private async void button1(object sender, EventArgs e)
{
    txtOutput.Text += "Auto-collecting variables. This may take several minutes";
    string v = await Task.Run(() => foo());
    txtOutput.Text += "\n" + v;
    string b = await Task.Run(() => bar());
    txtOutput.Text += "\n" + b;

    txtOutput.SelectionStart = txtOutput.Text.Length;
    txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}

你可以在C#4.0中做同样的事情:(第一个解决方案将由编译器转换为类似的东西。)

private  void button1(object sender, EventArgs e)
{
    txtOutput.Text += "Auto-collecting variables. This may take several minutes";
    Task.Factory.StartNew(() => foo())
        .ContinueWith(t => txtOutput.Text += "\n" + t.Result
            , TaskScheduler.FromCurrentSynchronizationContext())
        .ContinueWith(t => bar())
        .ContinueWith(t =>
        {
            txtOutput.Text += "\n" + t.Result;
            txtOutput.SelectionStart = txtOutput.Text.Length;
            txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
        }
            , TaskScheduler.FromCurrentSynchronizationContext());
}

答案 1 :(得分:3)

根据.NET的版本,您可以使用BackgroundWorker(Pre 4.0)或Tasks(Post 4.0 - 3.5 with a add-on)......仅举几例。

Backgroundworker Pseudocode:

var backgroundWorker = new BackgroundWorker()

method
{
    //Update UI
    backgroundWorker.RunWorkAsync()
}

asyncworkmethod
{
    //do main logic
}

asynccompletemethod
{
    //Update UI to say done
}

任务伪代码:

method
{
     //Update UI
     TaskFactory.StartNew(()=>DoWork).ContinueWith((previousTask)=>UpdateUIToSayDone)
}

而且,如果您使用4.5,那么您可以使用async/await关键字,但这只是围绕任务的语法糖(主要是......)。虽然如果你采用那种方法,Servy已经有了一个很好的例子

答案 2 :(得分:2)

使用BackgroundWorker类进行处理,而不会阻止UI更新。它具有可用于将进度信息传输到UI线程的事件。

答案 3 :(得分:0)

使用后台进程(阅读其他答案)是正确的方法,但如果您正在寻找一种非常快速的解决方法,则可以在更新TextBox后调用Application.DoEvents()。在大多数情况下,此调用将导致您的表单更新以反映您所做的更改。

答案 4 :(得分:0)

txtOutput.Update()应该做你想要的,但是你应该考虑使用后台线程来完成长时间运行的任务,而不会阻止UI线程。