簡體   English   中英

進度條未從后台工作者更新

[英]Progress bar not updating from background worker

我想要做的是將進度條作為單獨的 window,它將根據另一個表單上其他長時間處理工作的進度進行更新。 當我通過進度條代碼進行調試時,DoWork 和 ProgressChanged 都按預期被調用,但 UI 無法更新。 代碼甚至到達 Worker_RunWorkerCompleted 並關閉進度條。 我已經閱讀了大約 20 篇文章,但無法確定這個問題。 我知道我應該為 WPF 使用視圖 model 但下面的代碼被簡化以查看我是否可以讓進度條更新。 我知道我遺漏了一些簡單的東西,但我無法確定它是什么。

XAML:

<Window x:Class="CGXAcquisition.Forms.Dialog.FileProgressBar"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="File Upload Progress" Height="100" Width="300">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
        <TextBlock Text="{Binding ElementName=pbStatus, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Window>

背后的代碼:

public partial class FileProgressBar : Window, INotifyPropertyChanged
    {
        BackgroundWorker worker = new BackgroundWorker();

        private int fileLines = 0;
        private int totalFileLines = 0;

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public int FileLines
        {
            get { return fileLines; }
            set 
            { 
                fileLines = value;
                Percentage = (int)Math.Round((double)fileLines / TotalFileLines);
                OnPropertyChanged("FileLines");
            }
        }
        public int TotalFileLines
        {
            get { return totalFileLines; }
            set { totalFileLines = value; }
        }
        public int Percentage { get; set; }

        public FileProgressBar(int totalLines)
        {
            InitializeComponent();
            TotalFileLines = totalLines;
            Loaded += Window_ContentRendered;
        }
        private void Window_ContentRendered(object sender, EventArgs e)
        {
            
            worker.WorkerReportsProgress = true;
            worker.WorkerSupportsCancellation = true;
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
            worker.ProgressChanged += worker_ProgressChanged;
            worker.RunWorkerAsync();
        }

        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Close();
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            for(int i = 0; i < 100; i++)
            {
                Thread.Sleep(100);
                int percent = i / 100;
                worker.ReportProgress(percent, i);
            }
        }

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

    }

創建存在於另一種形式上的進度條的代碼:

private void ShowProgressBar()
        {
            progress = new FileProgressBar(FileInfo.LineCount);
            progress.Show();
        }

我在 OnLoaded 事件中調用 ShowProgressBar()

private void OnLoaded(object sender, RoutedEventArgs e)
        {
            ShowProgressBar();
        }

在我的構造函數中,我將事件處理程序添加到 Loaded 事件

Loaded += OnLoaded;

我希望看到 UI 使用我擁有的代碼進行更新。 我試過使用 Dispatcher 來更新 UI,但據我了解,worker_ProgressChanged 應該可以減少對調度程序的需求。 我查看了 StackOverflow、MSDN 和其他幾個博客,但沒有任何內容可以幫助我確定我遺漏了什么。

預先感謝您的幫助。

更新我終於注意到我的數學是錯誤的。 這一行 int percent = i / 100; 應該是整數百分比 ((int)(double)i / 100 * 100);

我知道我可以只使用 i 但我想做對。

Backgroundworker已棄用 API。相反,您應該盡可能使用Task.Run或異步 API。 要報告進度,您應該使用Progress<T> class。

您的代碼的主要問題:您的計算有誤。 目前,您的公式產生的最大值為1 ( 100/100 )。
此外, BackgroundWorker.ReportProgress接受一個int作為百分比參數。 double轉換為int的隱式類型將截斷double值的小數位。 因此, 0.9d變為0
這意味着ProgressBar.Value將一直為0直到i == 100 然后該值將為1並且您關閉Window

計算百分比的正確公式是: value / max_value * 100
同樣重要的是要理解,由於除法,您參與的數字變量或除數必須是double類型。 否則,由於從doubleint的隱式轉換,中間結果值將被截斷。 換句話說,你完全失去了小數位(沒有四舍五入)。

您沒有透露有關后台工作的任何詳細信息。 但是根據窗口的名稱,我假設您是從文件中讀取的。 在這種情況下,不要在后台線程上執行作業。 而是使用StreamReader的異步 API。

此外,控件從不實現INotifyPropertyChanged 它有幾個缺點:性能、屬性不能作為綁定目標、不能動畫等。
相反,控件或一般擴展DependencyObject的類型應該將它們的屬性實現為依賴屬性。

改進后的FileProgressBar實現和固定計算應如下所示:

FileProgressBar.xaml.cs

public partial class FileProgressBar : Window
{
  public int FileLines
  {
    get => (int)GetValue(FileLinesProperty);
    set => SetValue(FileLinesProperty, value);
  }

  public static readonly DependencyProperty FileLinesProperty = DependencyProperty.Register(
    "FileLines",
    typeof(int),
    typeof(FileProgressBar),
    new PropertyMetadata(default(int), OnFileLinesChanegd));

  // If this property is used for data binding it must be a dependency property too
  public int TotalFileLines { get; set; }

  // If this property is used for data binding it must be a dependency property too
  public int Percentage { get; set; }

  public FileProgressBar(int totalLines)
  {
    InitializeComponent();
    this.TotalFileLines = totalLines;
    this.Loaded += Window_ContentRendered;
  }

  private static void OnFileLinesChanegd(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    var this_ = d as FileProgressBar;
    this_.Percentage = (int)Math.Round((double)e.NewValue / this_.TotalFileLines * 100d);
  }

  private async void Window_ContentRendered(object sender, EventArgs e)
  {
    var progressReporter = new Progress<double>(progress => pbStatus.Value = progress);
    await DoWorkAsync(progressReporter);
    OnWorkCompleted();
  }

  private void OnWorkCompleted() => Close();

  async Task DoWorkAsync(IProgress<double> progressReporter)
    => await Task.Run(async () => await LongRunningTaskAsync(progressReporter));

  private static async Task LongRunningTaskAsync(IProgress<double> progressReporter)
  {
    for (int i = 0; i < 100; i++)
    {
      await Task.Delay(TimeSpan.FromMilliseconds(100));
      double percent = i / 100d * 100d;
      progressReporter.Report(percent);
    }
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM