TOC

This article has been localized into Russian by the community.

Дополнительные элементы:

Элемент управления ProgressBar

WPF поставляется с удобным элементом управления для отображения хода выполнения задачи, который называется ProgressBar. Для работы ProgressBar необходимо установить минимальное и максимальное значения а также величину приращения. 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 установить равным количеству файлов в списке, а затем просто увеличивать значение свойства Value для прохождения списка.

ProgressBar как и другие стандартные элементы управления WPF, отображается в соответствии с визуальным стилем операционной системы. Здесь, на 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 миллисекунд. К сожалению, как я уже описал, ничего не произойдет. Вот как это выглядит в середине процесса:

Обратите внимание, что курсор указывает на то, что что-то происходит, хотя индикатор выполнения по-прежнему выглядит так же, как и в начале (пустой). Как только цикл, представляющий нашу продолжительную задачу, будет завершен, индикатор выполнения будет выглядеть следующим образом:

Это действительно не помогло вашим пользователям увидеть ход выполнения! Для достижения нужного нам результата придется выполнить задачу в рабочем потоке, а затем отправить обновления в поток пользовательского интерфейса, который затем сможет немедленно обработать и отобразить эти обновления. Отличным инструментом для выполнения такой работы является класс 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;
		}
	}
}

Как вы можете видеть на скриншоте, отображение хода выполнения теперь обновляется на всем протяжении задачи, и, как показывает курсор, в потоке пользовательского интерфейса не выполняются интенсивные вычисления, что означает, что вы можете взаимодействовать с остальной частью интерфейса.

Пожалуйста, имейте в виду, что хотя BackgroundWorker действительно помогает с многими проблемами, связанными с многопоточностью, все же остаются некоторые вещи, о которых вам следует знать, поэтому, пожалуйста, взгляните на статьи о BackgroundWorker в этом учебнике, прежде чем делать что-либо более продвинутое, чем сценарий, подобный приведенному выше.

Неопределенный индикатор

Для некоторых задач выражение хода выполнения в процентах не представляется возможным или вы просто не знаете, сколько времени это займет. Для таких ситуаций был разработан неопределенный индикатор выполнения, где анимация позволяет пользователю знать, что что-то происходит, указывая, что время выполнения не может быть определено.

Индикатор выполнения WPF поддерживает этот режим с помощью свойства 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>

Обратите внимание, что зеленый индикатор хода выполнения не привязан ни к одной из сторон - вместо этого он свободно перемещается от начала до конца, а затем начинается все сначала.

ProgressBar с текстом

Одна вещь, которую я действительно пропустил, рассказывая о стандартном 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 имеет прозрачный фон по умолчанию, и это именно то, что нам нужно,

Мы используем привязку к данным, чтобы быть уверенными, что TextBlock покажет то же значение, что и ProgressBar. Обратите внимание на специальный синтаксис StringFormat, который позволяет нам показывать знак процента после числового значения - это может выглядеть немного странно, но, пожалуйста, обратитесь к статье StringFormat этого учебника для получения дополнительной информации об этом.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!