WPF带有一个方便的控件用于显示进度,称ProgressBar。它的工作原理就是设置最小值和最大值然后通过递增一个值,这样就可以直观的显示当前进度情况。下面我们将给出一个最简单的基本例程来演示该控件:
<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProgressBarSample" Height="100" Width="300">
<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Value="75" />
</Grid>
</Window>
在这个案例,我使用了一种非常标准的方法,以百分比显示进度(介于0和100%之间),初始值为75。另一种方法是显示执行任务列表中的实际最小值和最大值。 例如,如果在检查每个文件时循环访问收集的文件列表,则可以将Minimum属性设置为0,将Maximum设置为列表中的文件数量,然后在循环执行时增加。
与其他标准WPF控件一样,ProgressBar的渲染与操作系统的可视化样式相匹配。 在Windows 7上,它有一个很好的动画渐变,如屏幕截图所示。
上面的例子说明了使用ProgressBar是多么简单,但通常你希望显示一些实际工作的进度而不仅仅是静态值。
在大多数情况下,您将使用ProgressBar来显示某些繁重/漫长任务的进度,这就是大多数新程序员遇到一个非常常见的问题:如果您在UI线程上做了大量工作,同时尝试更新,例如一个ProgressBar控件,你很快就会意识到你不能同时在同一个线程上同时执行这两个操作。 或者更清楚一点,你可以,但ProgressBar实际上不会在任务完成之前显示进度的每次更新,这几乎使它变得毫无用处。
为了说明,您可以尝试以下示例:
<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTaskOnUiThread"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProgressBarTaskOnUiThread" Height="100" Width="300"
ContentRendered="Window_ContentRendered">
<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
</Grid>
</Window>
using System;
using System.Threading;
using System.Windows;
namespace WpfTutorialSamples.Misc_controls
{
public partial class ProgressBarTaskOnUiThread : Window
{
public ProgressBarTaskOnUiThread()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
for(int i = 0; i < 100; i++)
{
pbStatus.Value++;
Thread.Sleep(100);
}
}
}
}
一个非常基本的例子,一旦窗口准备好,我们就会从0到100进行循环,并且在每次迭代中,我们增加ProgressBar的值。 任何现代计算机都可以比你眨眼更快地做到这一点,所以我在每次迭代中添加了一个100毫秒的延迟。 不幸的是,正如我已经描述的,什么都不会发生。 这是它在过程中间的样子:
请注意,鼠标指针表明发生了某些事情,但ProgressBar看起来仍然像在开始时那样(空)。 只要完成表示我们漫长任务的循环,ProgressBar就会如下所示:
这真的没有帮助您的用户看到进度! 作为代替,我们必须在工作线程上执行任务,然后将更新推送到UI线程,然后UI线程将能够立即处理并直观地显示这些更新。 BackgroundWorker类是处理这项工作的一个很好的工具,我们在本教程的其他地方更多地讨论这个问题。 这是与上面相同的示例,但这次使用的是BackgroundWorker:
<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTaskOnWorkerThread"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProgressBarTaskOnWorkerThread" Height="100" Width="300"
ContentRendered="Window_ContentRendered">
<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
</Grid>
</Window>
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WpfTutorialSamples.Misc_controls
{
public partial class ProgressBarTaskOnWorkerThread : Window
{
public ProgressBarTaskOnWorkerThread()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i < 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i);
Thread.Sleep(100);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pbStatus.Value = e.ProgressPercentage;
}
}
}
正如您在屏幕截图中看到的那样,进度现在一直在通过任务进行更新,并且正如鼠标指针所示,UI线程上没有执行任何繁重的工作,这意味着您仍然可以与界面的其余部分进行交互。
请注意,尽管BackgroundWorker确实对多线程相关问题有很多帮助,但仍有一些事情你应该注意,所以在做一些比这个方案更高级的事情之前,请先看一下本教程中的BackgroundWorker章节。
对于某些任务,无法以百分比表示进度,或者您根本不知道需要多长时间。 对于这些情况,可是使用不确定的进度条,其中动画让用户知道发生了某些事情,同时指示无法确定运行时间。
WPF ProgressBar通过使用IsIndeterminate属性支持此模式,我们将在下一个示例中向您显示:
<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarIndeterminateSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProgressBarIndeterminateSample" Height="100" Width="300">
<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Name="pbStatus" IsIndeterminate="True" />
</Grid>
</Window>
请注意,绿色进度指示器不会固定到任何位置,而是从开始到结束滑动然后再次重新开始。
标准的WPF ProgressBar不能显示进度的文本。 对我们来说幸运的是,WPF的灵活性使我们很容易实现。 这是一个例子:
<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProgressBarTextSample" Height="100" Width="300">
<Grid Margin="20">
<ProgressBar Minimum="0" Maximum="100" Value="75" Name="pbStatus" />
<TextBlock Text="{Binding ElementName=pbStatus, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Window>
我们通过将ProgressBar和显示百分比的TextBlock放在同一个Grid中来完成上述操作,不指定任何行或列。 这将使TextBlock呈现在ProgressBar之上,这正是我们想要的,默认情况下TextBlock具有透明背景。
我们使用绑定来确保TextBlock与ProgressBar显示相同的值。 请注意特殊的StringFormat语法,它允许我们使用百分号后缀显示值 - 它可能看起来有点特殊,请参阅本教程的StringFormat章节以获取更多信息。