wpf 进度条未从后台工作进程更新

bvjveswy  于 2023-01-06  发布在  其他
关注(0)|答案(1)|浏览(169)

我尝试做的是将进度条作为一个单独的窗口,它将根据另一个表单上其他长时间处理工作的进度进行更新。DoWork和ProgressChanged都按预期调用,但UI无法更新。代码甚至到达Worker_RunWorkerCompleted并关闭进度条。我读了大约20篇文章,无法确定这个问题。我知道我应该使用WPF的视图模型,但是下面的代码被简化了,看看我是否可以只更新进度条。我知道我错过了一些简单的东西,但是我不能把我的手指放在它是什么。
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更新与我的代码。我已经尝试使用调度程序来更新UI,但据我所知,工人_ProgressChanged应该减轻调度程序的需要。我已经看了StackOverflow,MSDN和其他几个博客,没有帮助我确定我错过了什么。
预先感谢你的帮助。

    • 更新**我终于注意到我的计算错了,这一行int percent = i/100;应为整数百分比((整数)(双精度)i/100 * 100);

我知道我可以用i,但我想做对。

iyfamqjs

iyfamqjs1#

Backgroundworker是一个不推荐使用的API。相反,您应该尽可能使用Task.Run或异步API。要报告进度,您应该使用Progress<T>类。
您的代码的主要问题:计算错误。当前公式产生的最大值为1100/100)。
此外,BackgroundWorker.ReportProgress接受int作为百分比参数。从doubleint的隐式类型转换将截断double值的小数位。因此,0.9d变为0
这意味着ProgressBar.Valuei == 100之前一直是0,然后值将是1,并关闭Window
计算百分比的正确公式为:value / max_value * 100 .
同样重要的是要理解,由于除法,您参与的数值变量或除数必须是double类型。否则,由于从doubleint的隐式转换,中间结果值将被截断。换句话说,您完全丢失了小数位(没有舍入)。
你没有告诉任何关于你的后台任务的细节。但是从窗口的名字我假设你是从一个文件中读取的。在这种情况下,不要在后台线程上执行任务。而是使用StreamReader的异步API。
此外,控件从不实现INotifyPropertyChanged。它有几个缺点:性能、属性不能用作绑定目标、不能设置动画等。
相反,通常扩展DependencyObject的控件或类型应将其属性实现为依赖项属性。
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);
    }
  }
}

相关问题