sábado, 24 de julio de 2010

WPF: Integrando nuestra aplicación con la Barra de Tareas de Windows 7 (I)

Hola a todos!
En el último artículo, hablamos de lo importante que es aprovechar las características que nos ofrece el S.O. sobre el que nuestra aplicación se ejecuta, vimos como integrar nuestra aplicación con el sistema de energía de Windows 7 para hacerla “amigable” con el consumo.
Esta vez vamos a hablar de la barra de tareas de Windows 7, fue uno de los grandes cambios en el sistema operativo, y trae muchas ventajas  para facilitar la comunicación rápida e intuitiva entre nuestra aplicación y nuestros usuarios:

  1. IconOverlay: Esta propiedad de la barra de tareas nos permite colocar desde nuestro código un pequeño icono de 16x16 sobre el icono de nuestra aplicación, en la esquina inferior derecha, con lo que podemos avisar a nuestro usuario de cambios en el estado de la aplicación sin que tenga la pantalla mostrandose.

  2. ProgressBar: Muy util sin duda, nos permite colocar bajo el icono de nuestra aplicación una barra de progreso, con múltiples estados, capaz de comunicar a nuestro usuario el estado y progreso de una determinada tarea mientras usa otras aplicaciones, consiguiendo maximizar la productividad.

  3. Jumplist: Las Jumplist son enlaces rápidos a partes de nuestra aplicación que aparecen al hacer click derecho sobre el icono de la misma en la barra de tareas, podemos hacer que nuestra app vaya directamente a una ventana en concreto, sin haberla arrancado todavía.

  4. Thumbnail Toolbars: al pasar el ratón sobre los iconos de aplicaciones activas, Windows 7 nos ofrece una preview de la ventana de esa aplicación, con las Thumbnail Toolbars podemos añadir botones a esa vista que ejecuten ordenes en nuestra aplicación.
En este artículo vamos a centrarnos en las dos primeras: IconOverlay y ProgressBar, dejando las restantes para otro artículo.

.NET 3.5 vs .NET 4

Para acceder a todas estas características en .NET 3.5 solo teníamos un camino: Interoperabilidad con el api no manejada de Win32 (al igual que con el artículo sobre la gestión de energía), sin embargo para .NET 4 han integrado las funcionalidades de la barra de tareas de Windows 7 en el propio framework, por lo que el “uso y disfrute” de estas características es muchísimo más sencillo. Por ello el código de este artículo es solo útil para .NET 4.

Empecemos

Primero y como ya es costumbre, una imagen vale más que mil palabras, pues esta vez tenemos dos:
Tab001 tab002
Como veis me he puesto en plan original con los iconos…. y si, lo se, que yo me dedique al diseño es tan dañino como las armas de destrucción masiva…. pero por eso soy programador :)
Vamos a comenzar, lo primero es crear un nuevo proyecto WPF Application (File > New > Project), una vez creado, vamos a diseñar la pantalla principal en el archivo MainWindow.xaml
Los estilos que he usado en esta aplicación están disponibles en la descarga en el archivo Application.xaml, si tenéis dudas acerca de como usar / crear estilos, echadle un vistazo a mis dos artículos sobre este tema: aquí y aquí
Bien, para empezar a trabajar con la barra de tareas debemos añadir en xaml un objeto de tipo TaskbarItemInfo:
<Window.TaskbarItemInfo>
<TaskbarItemInfo/>
</Window.TaskbarItemInfo>


Con esto ya tendremos acceso a multitud de propiedades de la barra de tareas de windows 7 de forma sencilla y rápida. 

IconOverlay

Tab001


Vamos a empezar a trabajar con los IconOverlay, lo primero que debemos hacer es añadir como recurso en nuestro proyecto los archivos de imágen que queramos usar (png o ico por las transparencias).


Una vez hecho esto debemos crear un DrawingImage como recurso de nuestra ventana por cada imágen, en mi caso son 5:


<Window.Resources>
<DrawingImage x:Key="IconRed">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,16,16" 
ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
component/images/FireToy.ico" />
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="IconGreen">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,16,16" 
ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
component/images/GreenToy.ico" />
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="IconBlue">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,16,16" 
ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
component/images/BlueToy.ico" />
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="IconPink">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,16,16" 
ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
component/images/PinkToy.ico" />
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="IconBlack">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,16,16" 
ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
component/images/NinjaToy.ico" />
</DrawingImage.Drawing>
</DrawingImage>
</Window.Resources>


Una vez hecho esto, vamos a crear el TabItem con los botones y demás:


<TabItem Header="Taskbar IconOverlay" Name="TabItem1">
<Grid>
<Grid.Background>
<ImageBrush ImageSource="pack://application:,,,/
WPF Icon Overlay And ProgressBar;
component/images/overlay.png" 
Stretch="Uniform" Opacity=".2" />
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height=".33*"></RowDefinition>
<RowDefinition Height=".33*"></RowDefinition>
<RowDefinition Height=".33*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".2*"></ColumnDefinition>
<ColumnDefinition Width=".2*"></ColumnDefinition>
<ColumnDefinition Width=".2*"></ColumnDefinition>
<ColumnDefinition Width=".2*"></ColumnDefinition>
<ColumnDefinition Width=".2*"></ColumnDefinition>
</Grid.ColumnDefinitions>

<Label Grid.ColumnSpan="5" Grid.Row="0" VerticalContentAlignment="Center" 
HorizontalContentAlignment="Center" FontSize="24" Foreground="White"
Content="Seleccione una imagen para superponer">
</Label>

<Button Grid.Column="0" Grid.Row="1" Name="btnIconRed" Width="64" Height="64" 
Padding="0">
<Button.Content>
<Image Source="{StaticResource IconRed}" Width="56" Height="56" 
Stretch="Uniform" Margin="0"></Image>
</Button.Content>
</Button>
<Button Grid.Column="1" Grid.Row="1" Name="btnIconGreen" Width="64" Height="64" 
Padding="0">
<Button.Content>
<Image Source="{StaticResource IconGreen}" Width="56" Height="56" 
Stretch="Uniform" Margin="0"></Image>
</Button.Content>
</Button>
<Button Grid.Column="2" Grid.Row="1" Name="btnIconBlue" Width="64" Height="64" 
Padding="0">
<Button.Content>
<Image Source="{StaticResource IconBlue}" Width="56" Height="56" 
Stretch="Uniform" Margin="0"></Image>
</Button.Content>
</Button>
<Button Grid.Column="3" Grid.Row="1" Name="btnIconPink" Width="64" Height="64" 
Padding="0">
<Button.Content>
<Image Source="{StaticResource IconPink}" Width="56" Height="56" 
Stretch="Uniform" Margin="0"></Image>
</Button.Content>
</Button>
<Button Grid.Column="4" Grid.Row="1" Name="btnIconBlack" Width="64" Height="64" 
Padding="0">
<Button.Content>
<Image Source="{StaticResource IconBlack}" Width="56" Height="56" 
Stretch="Uniform" Margin="0"></Image>
</Button.Content>
</Button>                    

<Button Grid.ColumnSpan="5" Grid.Row="2" Name="btnIconReset" Width="125" 
Height="30" Content="Reset Overlay"></Button>
</Grid>
</TabItem>


Ya hemos terminado con todo el código xaml necesario para el IconOverlay, ahora vamos a por el código, lo primero es iniciar, en el evento Loaded de nuestra Window , el estado del IconOverlay a ninguno:


#Region "Window Code"
Private Sub MainWindow_Loaded(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles Me.Loaded
Me.TaskbarItemInfo.Overlay = Nothing
End Sub
#End Region


Como ves, acceder a las propiedades del icono de nuestra aplicación en la barra de tareas es muy sencillo gracias al objeto TaskbarItemInfo que hemos creado en Xaml.


En este caso asignando Nothing le indicamos que no tiene ningún icono asociado.


Ahora, en el evento Click de cada botón vamos a establecer el icono asociado al botón como el icono a colocar sobre la barra de tareas:


#Region "Taskbar IconOverlay"
Private Sub btnIconBlack_Click(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles btnIconBlack.Click
Me.TaskbarItemInfo.Overlay = CType(Resources("IconBlack"), ImageSource)
End Sub

Private Sub btnIconBlue_Click(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles btnIconBlue.Click
Me.TaskbarItemInfo.Overlay = CType(Resources("IconBlue"), ImageSource)
End Sub

Private Sub btnIconGreen_Click(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles btnIconGreen.Click
Me.TaskbarItemInfo.Overlay = CType(Resources("IconGreen"), ImageSource)
End Sub

Private Sub btnIconPink_Click(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles btnIconPink.Click
Me.TaskbarItemInfo.Overlay = CType(Resources("IconPink"), ImageSource)
End Sub

Private Sub btnIconRed_Click(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles btnIconRed.Click
Me.TaskbarItemInfo.Overlay = CType(Resources("IconRed"), ImageSource)
End Sub

Private Sub btnIconReset_Click(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles btnIconReset.Click
Me.TaskbarItemInfo.Overlay = Nothing
End Sub
#End Region


Bueno, no es muy complicado ¿no? Al principio del artículo creamos unos recursos de la ventana que eran del tipo DrawingImage, simplemente lo que hacemos en este código es convertir estos recursos al tipo ImageSource, que es el que espera la propiedad Overlay del objeto TaskbarItemInfo y asignarlos, el resultado es inmediato, si presionamos el botón que tiene asociado el avatar de fuego este será el resultado:


taskbar icon


Y si presionamos el botón Reset Overlay volverá a su estado normal:


taskbar no icon


Como veis es un código muy sencillo y que nos abre nuevas vías de comunicación con nuestros usuarios, podríamos por ejemplo mostrar un icono cuando se ha producido un error, si se require la intervención del usuario en una tarea o si hay nuevos datos, de esta forma nuestro usuario no dependería de estar viendo nuestra ventana para saber que pasa, podría estar consultando el correo, trabajando en excel o simplemente viendo algunas páginas de internet y se enteraría instantáneamente de la necesidad de intervenir en nuestra aplicación.


ProgressBar



tab002




Si el IconOverlay te ha parecido sencillo, el uso de la barra de progreso en el icono de la barra de tareas lo es más, vamos a empezar diseñando la pantalla:


<TabItem Header="Taskbar ProgressBar" Name="TabItem2">
<Grid>
<Grid.Background>
<ImageBrush ImageSource="pack://application:,,,/
WPF Icon Overlay And ProgressBar;
component/images/progressbar.png" 
Stretch="Uniform" Opacity=".2" />
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height=".25*"></RowDefinition>
<RowDefinition Height=".25*"></RowDefinition>
<RowDefinition Height=".25*"></RowDefinition>
<RowDefinition Height=".25*"></RowDefinition>
</Grid.RowDefinitions>

<Label Grid.Row="0" VerticalContentAlignment="Center" 
HorizontalContentAlignment="Center" FontSize="24" Foreground="White"
Content="Mueve el Slider para Aumentar/Disminuir">
</Label>

<ComboBox Grid.Row="1" Name="cmbEstilos" Height="30" Width="Auto" Margin="5" 
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ComboBox.Items>
<ComboBoxItem Name="ItemNormal" Content="Normal"/>
<ComboBoxItem Name="ItemError" Content="Error"/>
<ComboBoxItem Name="ItemStop" Content="Stop"/>
<ComboBoxItem Name="ItemIndeterminate" Content="Indeterminado"/>
<ComboBoxItem Name="ItemNone" Content="Ninguno"/>
</ComboBox.Items>
</ComboBox>

<ProgressBar Grid.Row="2" Name="Pbar" Height="30" Width="Auto" Margin="5" 
HorizontalAlignment="Stretch" VerticalAlignment="Center"
Minimum="0" Maximum="100" 
Value="{Binding Path=Value, ElementName=SlidValue}"></ProgressBar>

<Slider Grid.Row="3" Name="SlidValue" Height="30" Width="Auto" Margin="5" 
HorizontalAlignment="Stretch" VerticalAlignment="Center" 
Minimum="0" Maximum="100" Value="50" SmallChange="1">
</Slider>
</Grid>
</TabItem>


El código Xaml es muy sencillo, quizás lo más remarcable, y muy útil, es el Binding que le he aplicado a la propiedad Value de la progressBar, este binding apunta hacia la propiedad Value del control Slider, de esta forma, cada ver que movamos el Slider automáticamente se moverá la barra de progreso.


Como veis existe un Combo con varias opciones, estos son los estados que puede tener la barra de progreso en la barra de tareas:



  1. Normal: La barra de progreso aparece de forma normal, en color verde.


  2. Error: Igual que la normal, solo que el color de esta es rojo.


  3. Stop: La barra de progreso esta parada y no puede avanzar, el color es amarillo.


  4. Indeterminate: La barra no tiene un progreso determinado, hace una animación ciclica.


  5. None: No se muestra la barra de progreso.



Si os ponéis a pensar, seguro que ya se os están ocurriendo mil usos… un proceso largo en background que pueda tardar varios minutos, el usuario puede minimizar la aplicación y ver el progreso y resultado sin maximizarla, mientras trabaja en otra aplicación, puede ver cuando la tarea se ha detenido y necesita su atención (barra amarilla) cuando se ha producido un error (barra roja) o simplemente el progreso de la tarea, para ver si le da tiempo a tomar un cafe :), si combinamos esto con los IconOverlay, ¿os dais cuenta de la potencia que obtenemos de comunicación con nuestro usuario? Podemos hacerle conocer el estado de nuestra aplicación sin ni siquiera maximizar la ventana, mientras está haciendo otras cosas, se acabaron los tiempos muertos.


El código también es muy sencillo, en el evento Loaded establecemos la barra de progreso a None:


#Region "Window Code"
Private Sub MainWindow_Loaded(ByVal sender As Object, 
ByVal e As System.Windows.RoutedEventArgs) 
Handles Me.Loaded
Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.None
End Sub
#End Region


Y ahora controlamos el evento ValueChanged del control Slider para asignar el valor a nuestra barra de progreso en la barra de tareas:




Private Sub SlidValue_ValueChanged(ByVal sender As Object, 
ByVal e As System.Windows.RoutedPropertyChangedEventArgs(Of Double)) 
Handles SlidValue.ValueChanged
Me.TaskbarItemInfo.ProgressValue = SlidValue.Value / 100
End Sub


Dos cosas aquí, primero, no es necesario tener una progressbar en nuestra ventana para que funcione la progressbar de la barra de tareas, pero creo que es necesario de cara al usuario tener ambos métodos, para no obligarle a tener que mirar la barra de tareas si está viendo nuestra ventana. Segundo, la propiedad ProgressValue del objeto TaskbarItemInfo tiene un rango de 1 a 0, por lo que debemos dividir el valor de nuestro control Slider o nuestro progreso entre 100 y siempre mantener valores entre 0 y 100 para no tener problemas.


Por último vamos a controlar el evento SelectionChanged del combobox para cambiar nuestro tipo de barra de progreso al indicado en el desplegable:


Private Sub combo_SelectionChanged(ByVal sender As Object, 
ByVal e As System.Windows.Controls.SelectionChangedEventArgs) 
Handles cmbEstilos.SelectionChanged

Select Case DirectCast(e.AddedItems(0), System.Windows.Controls.ComboBoxItem).Content
Case "Normal"
SlidValue.IsEnabled = True
Pbar.IsIndeterminate = False
Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Normal
Case "Error"
SlidValue.IsEnabled = True
Pbar.IsIndeterminate = False
Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Error
Case "Stop"
SlidValue.IsEnabled = True
Pbar.IsIndeterminate = False
Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Paused
Case "Indeterminado"
SlidValue.IsEnabled = False
Pbar.IsIndeterminate = True
Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Indeterminate
Case "Ninguno"
SlidValue.IsEnabled = True
Pbar.IsIndeterminate = False
Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.None
End Select
End Sub


Y con esto nuestra aplicación ya será capaz de mostrar la barra de progreso en sus diferentes estilos:


Normal Error Pausa Indeterminado


Bueno, esto es todo por hoy, como podéis ver es un tema apasionante que nos abre nuevas alternativas de comunicación con el usuario, así que es nuestra obligación aprovecharlas, en el próximo artículo hablaremos de las Jumplist y los Thumbnail Toolbars, como nuevas formas de interactuar con nuestra aplicación.


Por si tenéis alguna dificultad, a parte por supuesto de poder contactar conmigo tanto dejando mensajes como en mi twitter, como en msdn o a mi email directamente, os dejo el proyecto completo para descarga, junto con los estilos y control templates que he usado.


Y recordad, con un simple comentario, podéis hacer feliz a un pequeño bloguero :)


Muchas gracias por leerme y Happy Coding!

No hay comentarios:

Publicar un comentario