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