Icono del sitio Programando a medianoche

Barra de progreso en el ícono de la aplicación en la barra de tareas de Windows® 7

Los que tienen la posibilidad de utilizar Windows 7 habrán notado que trae varias novedades como la vista en miniatura de las aplicaciones minimizadas al pasar el mouse sobre ellas, que algunas de éstas, como el Windows Media Player, en esa vista poseen botones, y otras como el Internet Explorer utilizan el ícono de la barra de tareas como indicador de progreso (en el IE se utiliza para mostrar el progreso de las descargas). Todas estas características y algunas más que aquí no nombro (por ejemplo, hay una característica gracias a la cual, con un dispositivo de tal sólo u$s 30, se puede medir la luz ambiental y, si lo programamos, se puede cambiar el tema de pantalla dependiendo de la misma) son programables a través de las API del sistema operativo para brindarle a nuestras aplicaciones todas las posibilidades que posee. En este artículo hablaré de la posibilidad de mostrar el progreso de una operación como fondo del ícono de la misma en la barra de tareas.
Las aplicaciones que corren sobre Windows 7 pueden mostrar el progreso de una tarea que están ejecutando en el ícono de la barra de tareas, evitando de esta manera que el usuario tenga que abrir la ventana de la misma para verificar el estado del proceso. A continuación detallo los posibles estados que se pueden mostrar:

Aplicación sin barra de progreso
Aplicación con estado normal y 50% de progreso
Aplicación con estado pausado y 50% de progreso
Aplicación con error y 50% de progreso
Aplicación con estado indeterminado

Para modificar el estado y el valor de progreso de nuestras aplicaciones podemos utilizar la interfaz ITaskbarList3 como se muestra a continuación

ITaskbarList3* pTL; //creado anteriormente
HRESULT hr = pTL->SetProgressState(hwnd, TBPF_NORMAL); //hwnd es el puntero de la ventana
pTL->SetProgressValue(50, 100); //Establecemos un 50% de progreso

Para los que desarrollamos en .NET este código en C no nos sirve de mucho, entonces podemos referenciar las clases e interfaces COM o, mejor aún, podemos utilizar las librerías “Windows® API Code Pack for Microsoft® .NET Framework” que ya tienen resuelto este tema. Estas librerías, junto con su código fuente y ejemplos, las podemos bajar de la siguiente dirección: http://code.msdn.microsoft.com/WindowsAPICodePack. Para cambiar el estado y progreso de nuestra aplicación debemos utilizar los métodos SetProgressState y SetProgressValue de la clase Microsoft.WindowsAPICodePack.Taskbar.TaskbarManager respectivamente.
A continuación muestro un ejemplo en WPF de un proyecto en el cual se cambia el progreso de la aplicación modificando el valor de un control Slider, y el estado de la misma a través de un ComboBox. El XAML de éste es el siguiente:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="TaskBarProgress.Window1"
    Title="Ejemplo de barra de progreso en la barra de tareas" Height="170" Width="400"
    WindowStartupLocation="CenterScreen" WindowStyle="SingleBorderWindow" MaxHeight="170"
    MinHeight="170" MinWidth="320" Icon="Scientia.ico">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" MinWidth="178" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition Height="Auto" MinHeight="36" />
        </Grid.RowDefinitions>
        <TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="15" FontWeight="Bold" Margin="5" Text="Estado de la aplicación:" />
        <ComboBox x:Name="ComboBox1" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="5" SelectionChanged="ComboBox_SelectionChanged">
            <ComboBoxItem>Normal</ComboBoxItem>
            <ComboBoxItem>Pausado</ComboBoxItem>
            <ComboBoxItem>Con error</ComboBoxItem>
            <ComboBoxItem>Indeterminado</ComboBoxItem>
            <ComboBoxItem IsSelected="True">Sin progreso</ComboBoxItem>
        </ComboBox>
        <TextBlock HorizontalAlignment="Right" FontSize="15" FontWeight="Bold" Grid.Row="1" VerticalAlignment="Center" Text="Porcentaje de progreso:" Margin="5" />
        <Slider x:Name="Slider1" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="5" SmallChange="1" Maximum="100" LargeChange="10" TickPlacement="Both" TickFrequency="5" ValueChanged="Slider_ValueChanged" />
        <TextBlock Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Center" Margin="10"><Run Text="Desarrollado por "/><Hyperlink NavigateUri="http://www.scientia.com.ar" RequestNavigate="Hyperlink_RequestNavigate"><Run Text="Scientia® Soluciones Informáticas"/></Hyperlink></TextBlock>
    </Grid>
</Window>

Y el código C# es el que muestro a continuación:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.WindowsAPICodePack.Taskbar;

namespace TaskBarProgress
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        private TaskbarManager windowsTaskbar = TaskbarManager.Instance;

        public Window1()
        {
            if ((Environment.OSVersion.Version.Major < 6) ||
               (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor < 1))
            {
                MessageBox.Show("Esta aplicación de ejemplo requiere Windows® 7 o superior para correr",
                    "Scientia® Soluciones Informáticas",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
                Close();
            }
            else
                InitializeComponent();
        }

        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
            e.Handled = true;
        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (ComboBox1.SelectedIndex != 4)
                windowsTaskbar.SetProgressValue(Convert.ToInt32(e.NewValue), 100, this);
        }

        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            switch ((sender as ComboBox).SelectedIndex)
            {
                case 0: //Normal
                    windowsTaskbar.SetProgressState(TaskbarProgressBarState.Normal, this);
                    break;
                case 1: //Pausado
                    windowsTaskbar.SetProgressState(TaskbarProgressBarState.Paused, this);
                    break;
                case 2: //Con error
                    windowsTaskbar.SetProgressState(TaskbarProgressBarState.Error, this);
                    break;
                case 3: //Indeterminado
                    windowsTaskbar.SetProgressState(TaskbarProgressBarState.Indeterminate, this);
                    return;
                case 4: //Sin progreso
                    windowsTaskbar.SetProgressState(TaskbarProgressBarState.NoProgress, this);
                    return;
            }

            windowsTaskbar.SetProgressValue(Convert.ToInt32(Slider1.Value), 100, this);
        }
    }
}

Obviamente todo el código mostrado funciona solamente sobre Windows® 7 por cual, si queremos hacer una aplicación que también sea compatible con versiones anteriores del sistema operativo, nos conviene verificar la versión de éste antes de utilizar estas características. Esto se puede hacer de la siguiente forma:

if ((Environment.OSVersion.Version.Major > 6) ||
    (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1)) {
        //Es Windows 7 o superior
} else {
        //Es un sistema operativo anterior a Windows® 7
}

Por último les dejo una solución con un proyecto WPF y otro con Windows Forms donde se muestra como utilizar esta funcionalidad.

Salir de la versión móvil