This article has been localized into Czech by the community.
Ovládací prvek ProgressBar
WPF obsahuje praktický ovládací prvek pro zobrazování průběhu, nazvaný ProgressBar. Funguje tak, že nastavíte minimální a maximální hodnotu a poté inkrementujete hodnotu, která poskytne vizuální indikaci o tom, jak daleko v procesu se aktuálně nacházíte. Zde je velmi základní příklad, který to demonstruje:
<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>
V tomto případě jsem použil poměrně standardní přístup k zobrazování průběhu jako procenta (mezi 0 a 100 %), přičemž jsem mu dal počáteční hodnotu 75. Další přístup spočívá v použití skutečných minimálních a maximálních hodnot ze seznamu úkolů, které provádíte. Například pokud projdete sbírku souborů a zkontrolujete každý z nich, můžete nastavit vlastnost Minimum na 0, Maximum na počet souborů ve vašem seznamu a poté hodnotu jen inkrementovat, jak procházíte seznamem.
"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.
Zobrazení progresu při provádění zdlouhavého úkolu
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říkladu:
<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.