WPF应用程序不确定进度条没有显示,但是进程正在后台运行

时间:2018-07-23 14:57:05

标签: c# wpf user-interface async-await

当我使进程正常工作时,当我尝试添加ProgressBar来向用户提供一些有关进程尚未崩溃的线索时,我可能遇到了线程问题。既然足够了,那么Indefinite ProgressBar对我来说就很好了。

 <ProgressBar x:Name="ProcessProgress" Minimum="0" Maximum="100" Visibility="Hidden" IsIndeterminate="False" Width="305" Height="20" Margin="240,214,248,10"></ProgressBar>

我尝试启动一个确定长度的进度条,该进度条在单击“开始”按钮之前不可见:

private void Start_Click(object sender, RoutedEventArgs e)
{
    ...
    //process starts here
    var fP = new FileProcessor();
    ProcessProgress.IsIndeterminate = true;
    ProcessProgress.Visibility = Visibility.Visible;

    ...
    //finally show the progressbar as full when done
    ProcessProgress.IsIndeterminate = false;
    ProcessProgress.Value = ProcessProgress.Maximum;

}

它只运行整个过程,而我的栏没有显示。

在此过程中如何生成进度条?

1 个答案:

答案 0 :(得分:1)

问题是所有代码都在UI线程中运行。 UI没有机会在整个过程完成之前进行自我更新。

您可以使用Task.Run()在后​​台运行繁重的工作,而不会阻塞UI。您可以使用await关键字等待该任务完成而不会阻塞UI。任务完成后,您将返回UI线程,您可以在其中再次修改UI。

对此事件处理程序的快速且肮脏的修复是:

private async void Start_Click(object sender, RoutedEventArgs e)
{
    ProcessProgress.IsIndeterminate = true;
    ProcessProgress.Visibility = Visibility.Visible;
      ...
    await Task.Run(()=>
    {
        //process starts here
        var fP = new FileProcessor();

        ...
    });

    //We are back in the UI thread, we can modify the UI
    ProcessProgress.IsIndeterminate = false;
    ProcessProgress.Value = ProcessProgress.Maximum;
}

无需使用Invoke返回到UI线程,这就是await本身的工作。

关于async void的注释。它仅用于事件处理程序或类似方法。您不能等待async void方法,这意味着如果出现问题,您甚至无法获得任何异常。不返回任何内容的异步方法应具有async Task签名。

如果要报告进度,可以使用IProgress界面和Progress类,如here所述。 Progress类将在其创建的线程上调用回调或引发事件。有效载荷可以是任何类别,而不仅仅是百分比

最好将报告代码移到单独方法中,以保持按钮处理程序的清洁。代码看起来像这样:

//The progress class
class FileProgress
{
    public string FileName{get;set;}
    public int Progress{get;set;}
    public string Message{get;set;}

    public FileProgress(string fileName,int progress,string message)
    {
        FileName=filename;
        Progress=progress;
        Message=message;
    }
}

//In the form itself
private void ReportStart()
{
    ProcessProgress.IsIndeterminate = true;
    ProcessProgress.Visibility = Visibility.Visible;
}

private void ReportEnd()
{
    ProcessProgress.IsIndeterminate = false;
    ProcessProgress.Value = ProcessProgress.Maximum;
}

private void ReportProgress(FileProgress progress)
{
    ProcessProgress.Value =progress.Progress;        
    PanelStatus.Text = $"Working on {progress.FileName} : {progress.Message}";
}

事件处理程序现在看起来像这样:

private async void Start_Click(object sender, RoutedEventArgs e)
{
    ReportStart();

    IProgress<FileProgress> progress=new Progress<FileProgress>(ReportProgress);
      ...
    await Task.Run(()=>
    {
        //process starts here
        var fP = new FileProcessor();

        foreach(var file in someFiles)
        {
              progress.Report(new FileProgress(file,0,"Starting");
              //Do some processing
              progress.Report(new FileProgress(file,100,"Finished");
        }
        ...
    });

    //We are back in the UI thread, we can modify the UI
    ReportEnd();
}