This article is currently in the process of being translated into Spanish (~99% done).
The ProgressBar control
WPF viene con un control práctico para mostrar progreso, llamado ProgressBar. Funciona estableciendo un valor mínimo y un valor máximo y luego, incrementando un valor mostrará visualmente el progreso que tiene actualmente. Aquí hay un ejemplo para demostrarlo con:
<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>
En este caso, he usado un enfoque bastante estándar para mostrar el progreso como un porcentaje (entre 0 y 100%), dándole un valor inicial de 75. Otro enfoque consiste en utilizar valores mínimos y máximos reales de una lista de tareas que está realizando. Por ejemplo, si recorre una lista recopilada de archivos mientras verifica cada uno de ellos, puede establecer la propiedad Mínimo en 0, el Máximo en la cantidad de archivos en su lista, y luego simplemente incrementar a medida que avanza.
ProgressBar, al igual que otros controles WPF estándar, se representa para que coincida con el estilo visual del sistema operativo. Aquí en Windows 7, tiene un buen gradiente animado, como se ve en la captura de pantalla.
Mostrando progreso mientras realiza una tarea larga.
El ejemplo anterior ilustra lo simple que es usar una barra de progreso, pero normalmente, por supuesto, querrá mostrar el progreso de un trabajo real y no solo un valor estático.
En la mayoría de las situaciones, usará la barra de progreso para mostrar el progreso de algunas tareas pesadas / largas, y aquí es donde la mayoría de los nuevos programadores se encuentran con un problema muy común: si realiza un trabajo pesado en el hilo de la interfaz de usuario, mientras intenta actualizar simultáneamente, por ejemplo un control ProgressBar, pronto se darás cuenta de que no puede hacer ambas cosas, al mismo tiempo, en el mismo hilo. O para ser más claro, puede hacerlo, pero ProgressBar en realidad no mostrará cada actualización en el progreso antes de que se complete la tarea, lo que prácticamente la deja inútil.
Para ilustrarlo, puede utilizar el siguiente ejemplo.
<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);
}
}
}
}
Un ejemplo muy básico, donde, tan pronto como la ventana está lista, hacemos un ciclo de 0 a 100 y en cada iteración, incrementamos el valor de ProgressBar. Cualquier computadora moderna puede hacer esto más rápido de lo que puede parpadear, por lo que he agregado un retraso a cada iteración de 100 milisegundos. Lamentablemente, como ya he descrito, no pasará nada. Así es como se ve en el medio del proceso:
Observe que el cursor indica que algo está sucediendo, sin embargo, la barra de progreso todavía se ve como al principio (vacía). Tan pronto como el bucle, que representa nuestra larga tarea, está hecho, la barra de progreso se verá así:
¡Eso realmente no ayudó a sus usuarios a ver el progreso! En cambio, tenemos que realizar la tarea en un subproceso de trabajo y luego enviar actualizaciones al subproceso de la interfaz de usuario, que podrá procesar y mostrar visualmente estas actualizaciones de inmediato. Una excelente herramienta para manejar este trabajo es la clase BackgroundWorker, de la cual hablaremos mucho más en otro lugar en este tutorial. Aquí está el mismo ejemplo que el anterior, pero esta vez usando un 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;
}
}
}
Como puede ver en la captura de pantalla, el progreso ahora se actualiza a lo largo de la tarea y, como indica el cursor, no se realiza ningún trabajo duro en el hilo de la interfaz de usuario, lo que significa que aún puede interactuar con el resto de la interfaz.
Tenga en cuenta que si bien BackgroundWorker ayuda mucho con problemas relacionados con subprocesos múltiples, todavía hay algunas cosas que debe tener en cuenta, así que eche un vistazo a los artículos de BackgroundWorker en este tutorial antes de hacer algo más avanzado que un escenario como el anterior.
Indeterminado
Para algunas tareas, no es posible expresar el progreso como un porcentaje o simplemente no sabe cuánto tiempo llevará. Para esas situaciones, se ha inventado una barra de progreso indeterminada, donde una animación le permite al usuario saber que algo está sucediendo, mientras indica que el tiempo de ejecución no se puede determinar.
La barra de progreso de WPF admite este modo mediante el uso de la propiedad IsIndeterminate, que le mostraremos en el siguiente ejemplo:
<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>
Tenga en cuenta que el indicador de progreso verde no está anclado a ninguno de los lados, sino que flota libremente de principio a fin y luego comienza de nuevo otra vez.
ProgressBar con texto
Una cosa que realmente extrañé de la barra de progreso estándar de WPF es la capacidad de mostrar una representación de texto del progreso, así como la barra de progreso. Afortunadamente para nosotros, la flexibilidad de WPF hace que sea realmente fácil de lograr. Aquí hay un ejemplo:
<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>
Logramos lo anterior colocando ProgressBar y TextBlock mostrando el porcentaje dentro de la misma Cuadrícula, sin especificar ninguna fila o columnas Esto hará que TextBlock se encuentre encima de ProgressBar, que es exactamente lo que queremos aquí, porque TextBlock tiene un fondo transparente por defecto.
Usamos un enlace para asegurarnos de que TextBlock muestre el mismo valor que ProgressBar. Observe la sintaxis especial StringFormat , que nos permite mostrar el valor con un postfix de signo de porcentaje; puede parecer un poco extraño, pero consulte el artículo StringFormat de este tutorial para más información al respecto.