用于读取文件的进度条 - 意外的UI行为

时间:2015-06-09 13:58:56

标签: wpf progress-bar backgroundworker

我正在尝试在读取文件时更新进度条。 文件大小将在200Kb到50Mb之间变化。

我正在使用System.ComponentModel.BackgroundWorker进行阅读过程,具有以下定义:

progressBar.Minimum = 0

progressBar.Maximum = System.IO.FileInfo.Length(我不关心百分比)。

阅读过程:

void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bg = sender as BackgroundWorker;

        while (!reader.EndOfStream)
                {
                    line = reader.ReadLine();
                    file_content.Add(line);
                    progress_precentage += line.Length + 2;
                    System.Threading.Thread.Sleep(100);
                    bg.ReportProgress(progress_precentage);
                }
    }

更新过程:

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar.Value = e.ProgressPercentage;

        labelProgress.Content = "reading " + e.ProgressPercentage + " out of " + file_length + " bytes";
    }
UI的反应非常奇怪。 对于300Kb文件,进度条和标签甚至没有更新。它们立即达到最大值。 对于50Mb文件,它们会在完成一秒钟之前更新4次。

所以我添加了System.Threading.Thread.Sleep:

while (!reader.EndOfStream)
                {
                    line = reader.ReadLine();
                    file_content.Add(line);
                    progress_precentage += line.Length + 2;
                    System.Threading.Thread.Sleep(100);
                    bg.ReportProgress(progress_precentage);
                }

这导致300Kb文件大约需要一分钟才能完成,而50Mb文件则需要大概一分钟。

当我使用System.Threading.Thread.Sleep(1)时,300Kb文件大约快一半,实际上是慢下来直到大约5秒完成。 50Mb文件需要很长时间才能完成。

当然我可以摆弄Thread.Sleep所以它会每10行左右触发一次,但性能会根据文件大小而改变。

有没有办法考虑文件大小,以便无论文件大小如何,过程将在2~3秒内完成?我知道这是可能的,因为读取一个50Mb的文件需要不到一秒的时间才能完成(没有Thread.Sleep)。

谢谢!

修改 建议后的代码(由于某种原因无法将其作为答案提交):

void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bg = sender as BackgroundWorker;

        try
        {
            file_content = System.IO.File.ReadAllLines(file_path).ToList();
        }
        catch ()
        {
            bg.ReportProgress(-1);
            file_read_successful = false;
            return;
        }

        //For i from 0 to 100
        System.Threading.Thread.Sleep(10);
        bg.ReportProgress(i);

        file_read_successful = true;
    }

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)        {
        //Failure to read file
        if (e.ProgressPercentage < 0)
        {
            //Show popup with failure message
            textBlockFailure.Text = (string)e.UserState;
            popupSelect.IsOpen = true;
            return;
        }

        labelProgress.Content = e.ProgressPercentage + "%";
        progressBar.Value = e.ProgressPercentage;
    }

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (file_read_successful)
        {
            labelProgress.Content = "Done!";
            progressBar.Value = progressBar.Maximum;
        }
        else
        {
            labelProgress.Content = "";
            progressBar.Value = progressBar.Minimum;
        }
        //Unregister events
        worker.DoWork -= worker_DoWork;
        worker.ProgressChanged -= worker_ProgressChanged;
        worker.RunWorkerCompleted -= worker_RunWorkerCompleted;
    }

3 个答案:

答案 0 :(得分:1)

由于您尝试使进度条占用最小 3秒,无论文件大小,带宽,主机或客户端计算机上的其他进程等等,嗯,实际上只有两个选择。

第一种选择是即使在下载完成后也可以继续进度条。这有几个选项(显示100%完成剩余时间,操作以便在下载后返回不真实的值等)。

第二种选择是限制实际下载,正如您已经练习过的那样。同样,这里有许多因素可以控制您的代码。因此,我建议添加一些计算,以便了解如何节流。

要详细说明第二种选择:您已经通过限制下载所需时间的百分比来显示基本方法。您可以通过预先读取文件大小并从中进行计算来构建此基础。另一种选择是对文件进行部分下载(例如,1000行),查看需要多长时间,并推断猜测下载整个文件需要多长时间。

作为一个例子,这可能是多么困难 - 如果您已经看到MS操作系统复制文件并显示&#34;剩余时间,&#34;即使在文件传输过程中,这种情况经常是正确的还是一致的?

当然,您并未计算剩余时间,而是显示进度条。但我会坚持认为你正在遇到同样的根本障碍。

答案 1 :(得分:0)

在阅读完问题之后,我注意到的第一件事是FileInfo.Length属性类型为longProgressBar.Maximum属性属于double类型,所以你那里很容易出问题。

接下来我注意到的是你正在调用Thread.Sleep(100);,这是一个糟糕的主意。 Thread.Sleep方法将阻止UI线程,因此不是暂停执行的好方法。而不是那样,你应该尝试使用Task.Delay method

接下来,我注意到您对progress_precentage += line.Length + 2的调用将导致总progress_precentage与您之前设置的Maximum值不匹配。如果您解决了这些问题,可能会有所帮助。

答案 2 :(得分:0)

谢谢大家的回复。

经过一夜安眠和Tony Hinkle的回应,我决定限制文件读取确实是一个坏主意。 所以我做的是读取文件,然后使用Thread.Sleep(10)更新进度条,大约需要。 2秒钟完成。

用户只能看到50Mb文件的轻微延迟,而且对于较小的文件都没有。

它有点作弊,但整体上是一个快速且用户界面友好的解决方案。

Aaron Thomas在回复中的第一选择提供了实施的总体思路,因此它是公认的答案。

再次感谢您的建议!

相关问题