TOC

This article is currently in the process of being translated into Czech (~76% done).

Misc. controls:

The ProgressBar control

WPF comes with a handy control for displaying progress, called the ProgressBar. It works by setting a minimum and maximum value and then incrementing a value, which will give a visual indication on how far in the process you currently are. Here's a very basic example to demonstrate it with:

<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>

In this case, I've used a pretty standard approach of showing progress as a percentage (between 0 and 100%), giving it an initial value of 75. Another approach is to use actual minimum and maximum values from a list of tasks you're performing. For instance, if you loop through a collected list of files while checking each of them, you can set the Minimum property to 0, the Maximum to the amount of files in your list, and then just increment as you loop through it.

"ProgressBar", stejně jako i ostatní ovládací prvky WPF, mají vizuální styl operačního systému. Na obrázku výše můžeme vidět styl Windows 7, s animovaným barevným přechodem.

Showing progress while performing a lengthy task

Příklad výše ilustruje základní použití ovládacího prvku "ProgressBar" s pevně danou hodnotou průběhu. Za normálních okolností se ovšem používá dynamická hodnota, která odpovídá průběhu určité akce nebo procesu.

Ovládací prvek "ProgressBar" se většinou používá tam, kde je nutné zobrazit průběh určité dlouho trvající operace. Zde mnoho začínajících programátorů narazí na problém. Pokud na UI vlákně běží náročná úloha, nelze zároveň v té chvíli aktualizovat samotnou hodnotu průběhu. Aktualizovaná hodnota se zobrazí až v momentě, kdy se operace dokončí. Běžící úloha totiž brání překreslení samotného ovládacího prvku.

Pro ilustraci tohoto problému se podívejme na následující příklad:

<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);
			}
		}
	}
}

Velice jednoduchý příklad, ve kterém se pohybujeme v cyklu od 0 do 100. V každé iteraci tohoto cyklu zvýšíme hodnotu průběhu o 1. Protože moderní počítače zvládnou tuto úlohu vykonat během mrknutí oka, přidáme pro názornost do každé iterace ještě zpoždění 100 milisekund. Jak můžete vidět, nic se neděje a samotná hodnota se neaktualizuje.

Všimněte si kurzoru, který indikuje, že se něco děje. "ProgressBar" ovšem i přesto zůstává stejný, jako na začátku startu aplikace. Jakmile dojde k dokončení probíhajícího cyklu, "ProgressBar" vypadá následovně:

Takové chování pro koncového uživatele moc užitku nemá. Řešením je dlouhotrvající úlohu spustit na pracovním vlákně a všechny aktualizace převádět na UI vlákno dodatečně. Tak bude mít UI vlákno dostatek času všechny změny zpracovat a zobrazit. Pomocníkem v této situaci může být například třída "BackgroundWorker", která je detailně popsána v jiné kapitole tohoto tutoriálu. Podívejme se tedy na stejný příklad znovu, ale nyní s využitím třídy "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;
		}
	}
}

Jak můžete vidět na obrázku, indikace průběhu se nyní aktualizuje po celou dobu trvání operace. Kurzor také naznačuje, že na UI vlákně neprobíhá žádná dlouhotrvající operace. Celou aplikaci lze proto ovládat bez omezení.

Mějte ovšem na paměti, že ačkoliv třída "BackgroundWorker" může pomoci v situacích jako je tato, existuje celá řada dalších okolností, které mohou mít na chování aplikace ve více vláknovém prostředí vliv. Při jiném než tomto základním použití, si proto projděte kapitoly tutoriálu, které se této problematice věnují.

Indeterminate - neurčitý stav

V některých případech nelze průběh úlohy vyjádřit v procentech. Třeba už jen z toho důvodu, že nevíme, kolik času taková operace zabere. Pro tyto situace existuje pro "ProgressBar" speciální, tzv. neurčitý stav. Ten koncovému uživateli indikuje že se něco děje, a že hodnotu průběhu nelze v tomto případě rozumně vyjádřit.

Ovládací prvek "ProgressBar" tento stav nastavuje na základě hodnoty "True" (ano) nebo "False" (ne) vlastnosti IsIndeterminate (v českém překladu jako "je neurčitého stavu"), kterou použijeme v následujícím příkladě:

<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>

Všimněte si animovaného průběhu, který přechází od začátku do konce. Právě ten indikuje tzv. neurčitý stav.

"ProgressBar" s textem

Jednou z věcí, která v základním ovládacím prvku "ProgressBar" chybí, je schopnost zobrazit textovou reprezentaci samotného průběhu.

<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>

Pro zobrazení textu, jako v příkladu výše, můžeme použít "TextBlock", který umístíme do stejného panelu jako "ProgressBar". Bez specifikace sloupce nebo řádku dojde k vykreslení textu nad "ProgressBar", což je přesně to, o co v příkladu jde. Díky průhledné barvě (výchozí) pozadí prvku "TextBlock", můžeme vidět jak grafickou, tak i textovou reprezentaci průběhu najednou.

Pro zobrazení aktuálního průběhu přímo v textu, použijeme vazby (binding) na hodnotu prvku "ProgressBar". Všimněte si také použití speciálního formátu StringFormat pro zobrazený text s procentem na konci. Jedná se o zápis formátu, kterému se věnuje celá kapitola (StringFormat) tohoto tutoriálu.

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!