martes, 3 de agosto de 2010

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

Hola!
En el artículo anterior sobre integración de nuestra aplicación con la barra de tareas de Windows 7 (puedes verlo aquí) hablábamos sobre las formas que nos ofrece la barra de tareas de Windows 7 de comunicar información a nuestros usuarios, usando la sobreimpresión de iconos y la barra de progreso.
En todo programa la comunicación es bidireccional, por un lado comunicamos información al usuario y por otro esperamos que el usuario nos proporcione información con la que trabajar, ya que hemos conseguido adaptar a Windows 7 la forma en la que nuestro usuario recibe información, hagamos ahora que pueda de forma sencilla e intuitiva comunicarnos información desde la barra de tareas.

Para esto, en este artículo, trataremos sobre las JumpList y los Thumbnail Toolbars:
  1. 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.
  2. 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.

.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

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.


JumpList



JumpList 


Vamos a empezar a trabajar con las JumpList, lo primero que tenemos que hacer es abrir el código del archivo application.xaml y añadir un nuevo manejador de eventos para el evento Startup:


Private Sub Application_Startup(ByVal sender As Object, 
ByVal e As System.Windows.StartupEventArgs) 
Handles Me.Startup
End Sub


La forma en la que los elementos de la Jumplist se comunicarán con nuestra aplicación es mediante argumentos a la hora de ejecutarla, por lo que en el evento Startup realizaremos dos trabajos, por un lado comprobaremos si nos ha llegado un parámetro y ejecutaremos el código asociado al mismo y por otro lado en este evento crearemos nuestra lista y se la notificaremos a la aplicación.


Para controlar los argumentos pasados a la aplicación usaremos la propiedad Args de la variable e del evento, usando un bloque Select Case para ello de la siguiente forma:


If e.Args.Count > 0 Then
Select Case e.Args(0)
Case "update"
MessageBox.Show("No existen actualizaciones")
Case "searchclient"
MessageBox.Show("Has seleccionado Buscar Clientes")
Case "searchinvoice"
MessageBox.Show("Has seleccionado Buscar Facturas")
End Select
End If


Como podéis ver es muy simple, al crear nuestra JumpList a cada Elemento le hemos asociado un argumento (update, searchclient o searchinvoice) al seleccionar un elemento este ejecuta nuestra aplicación enviándole el argumento que le hemos asociado y nosotros al recibirlo podemos ejecutar el código pertinente.


Ahora vamos a crear los elementos de la JumpList, para esto usaremos el objeto JumpTask del namespace Shell:


Dim BuscarActualizaciones As New Shell.JumpTask
BuscarActualizaciones.Title = "Buscar Actualizaciones"
BuscarActualizaciones.Arguments = "update"
BuscarActualizaciones.Description = "Comprobamos si existen actualizaciones."
BuscarActualizaciones.CustomCategory = "Mantenimiento"
BuscarActualizaciones.IconResourcePath = Assembly.GetEntryAssembly().CodeBase
BuscarActualizaciones.ApplicationPath = Assembly.GetEntryAssembly().CodeBase

Dim BuscarCliente As New Shell.JumpTask
BuscarCliente.Title = "Buscar Cliente"
BuscarCliente.Arguments = "searchclient"
BuscarCliente.Description = "Busqueda para localizar un cliente."
BuscarCliente.CustomCategory = "Clientes"
BuscarCliente.IconResourcePath = Assembly.GetEntryAssembly().CodeBase
BuscarCliente.ApplicationPath = Assembly.GetEntryAssembly().CodeBase

Dim ConsultarFacturas As New Shell.JumpTask
ConsultarFacturas.Title = "Consultar Facturas"
ConsultarFacturas.Arguments = "searchinvoice"
ConsultarFacturas.Description = "Ver la lista de facturas"
ConsultarFacturas.CustomCategory = "Facturación"
ConsultarFacturas.IconResourcePath = Assembly.GetEntryAssembly().CodeBase
ConsultarFacturas.ApplicationPath = Assembly.GetEntryAssembly().CodeBase


Simplemente definimos las características de cada JumpTask, ahora solo nos queda agregarlos a una JumpList:


Dim Lista As New Shell.JumpList
Lista.JumpItems.Add(BuscarActualizaciones)
Lista.JumpItems.Add(BuscarCliente)
Lista.JumpItems.Add(ConsultarFacturas)


Por último, tenemos que indicar al sistema que relacione esta Lista con nuestra aplicación, usando el método SetJumpList de la clase JumpList:


Shell.JumpList.SetJumpList(Application.Current, Lista)


Aunque este código se ejecutará siempre que arranquemos la aplicación, nunca se duplicarán los elementos de la JumpList pues el sistema se encargará cada vez que le pasemos una JumpList de actualizar los elementos que ya tiene asignados, en caso de que hayamos realizado algún cambio, de crear los nuevos elementos o de eliminar los elementos que ya no existan.


Con esto si ejecutas la aplicación y presionas con el botón derecho en el icono de la misma en la barra de tareas verás los elementos creados por nosotros, si seleccionas alguno verás que aparece el mensaje correspondiente y se abre una nueva instancia de la aplicación:


jumplist2


En conjunto, el dotar a nuestra aplicación de JumpList no es un proceso complicado, pero a la larga puede darnos muchos beneficios, ¿porque hacer pasar a nuestros usuarios por varias ventanas para llegar a consultar o actualizar un dato, cuando podemos ofrecerles las acciones más comunes simplemente con dos clicks y que nuestra aplicación arranque directamente a donde nuestros usuarios necesitan ir? Si lo piensas detenidamente seguro que se te ocurren mil situaciones donde te abría sido muy cómodo tener este sistema a mano. Por lo menos en mi experiencia personal esta es una de las características de Windows 7 que más útil me resulta.


Thumbnail Toolbars



Toolbar


Esta forma de que el usuario interactúe con nuestra aplicación se combina de forma perfecta con el contenido de nuestro anterior artículo, en el que explicábamos como informar al usuario del progreso de nuestra aplicación sin que tuviese la ventana de la misma a la vista, pues bien, con los thumbnail Toolbars, permitiremos que, una vez recibida esa información el usuario pueda responder ejecutando una orden en nuestra aplicación sin la necesidad de maximizar la ventana.


Lo primero que vamos a hacer es crear las imágenes para los botones, usando la clase DrawingImage, como recursos de nuestra ventana:


<Window.Resources>
<DrawingImage x:Key="IconUpdate">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,24,24" 
ImageSource="/Images/update.png" />
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="IconClient">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,24,24" 
ImageSource="/Images/client.png" />
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="IconInvoice">
<DrawingImage.Drawing>
<ImageDrawing Rect="0,0,24,24" 
ImageSource="/Images/invoice.png" />
</DrawingImage.Drawing>
</DrawingImage>
</Window.Resources>


La clase que usaremos para crear nuestros botones es el TaskBarItemInfo.ThumbButtonInfo:


<Window.TaskbarItemInfo>
<TaskbarItemInfo>
<TaskbarItemInfo.ThumbButtonInfos>
<ThumbButtonInfo x:Name="btnUpdate"
ImageSource="{StaticResource IconUpdate}"
Description="Comprueba actualizaciones"
IsBackgroundVisible="True">

</ThumbButtonInfo>
<ThumbButtonInfo x:Name="btnClient"
ImageSource="{StaticResource IconClient}"
Description="Ver los clientes"
IsBackgroundVisible="True">
</ThumbButtonInfo>
<ThumbButtonInfo x:Name="btnInvoice"
ImageSource="{StaticResource IconInvoice}"
Description="Ver las facturas"
IsBackgroundVisible="True">
</ThumbButtonInfo>
</TaskbarItemInfo.ThumbButtonInfos>
</TaskbarItemInfo>
</Window.TaskbarItemInfo>


La clase ThumbButtonInfo tiene varias propiedades que nos permitirán personalizar la apariencia de nuestro botón:



  • Description: Indica el texto que aparecerá sobre el botón al dejar el ratón sobre él.


  • IsInteractive: Esta propiedad permite usar un botón de dos formas:


    • True: el botón se comporta como un botón normal, este es el comportamiento por defecto.


    • False: el botón no es interactivo y solo se muestra al usuario, no reaccionando a las acciones de este.



  • IsBackgroundVisible: Indica si se resalta el botón al posicionar el ratón sobre el o no, por defecto es True.


  • DismissWhenClicked: Si establecemos a True esta propiedad, cuando pulsemos sobre el botón el Thumbnail se cerrará, si es False quedará abierto hasta que el ratón salga del icono de la barra de tareas, por defecto es False.



Ya tenemos creados nuestros botones, si ejecutas la aplicación y pasas el botón sobre el icono de la barra de tareas verás que aparecen los 3 botones que hemos creado, pero si haces click sobre ellos no ocurre nada.


Ahora debemos programar el comportamiento de los botones, esto lo podemos realizar de dos formas diferentes: usando eventos como cualquier otro botón o usando comandos.


Los dos métodos pueden realizar el mismo trabajo pero quizás es más recomendable usar comandos, pues por regla general estos botones serán réplicas de otros botones posicionados en la Toolbar principal de la aplicación o opciones de menús y usando comandos podemos enlazar al mismo código ambos, aun así voy a explicar un poco los dos casos.


Usando Eventos



El funcionamiento de los ThumbnailButtonInfo con eventos es muy sencillo y exactamente igual al resto de controles de nuestra aplicación, simplemente añadimos el evento al que queremos responder desde xaml:


<ThumbButtonInfo x:Name="btnUpdate"
ImageSource="{StaticResource IconUpdate}"
Description="Comprueba actualizaciones"
IsBackgroundVisible="False"
Click="btnUpdate_Click">
</ThumbButtonInfo>
<ThumbButtonInfo x:Name="btnClient"
ImageSource="{StaticResource IconClient}"
Description="Ver los clientes"
IsBackgroundVisible="True"
Click="btnClient_Click">
</ThumbButtonInfo>
<ThumbButtonInfo x:Name="btnInvoice"
ImageSource="{StaticResource IconInvoice}"
Description="Ver las facturas"
IsBackgroundVisible="True"
IsEnabled="False"
Click="btnInvoice_Click">
</ThumbButtonInfo>


Como veis en cada botón hemos añadido un evento Click, ahora en código escribimos el código necesario en cada manejador de eventos:


Private Sub btnUpdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
MessageBox.Show("Click en actualizar mediante eventos")
End Sub

Private Sub btnClient_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
MessageBox.Show("Click en clientes mediante eventos")
End Sub

Private Sub btnInvoice_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
MessageBox.Show("Click en facturas mediante eventos")
End Sub


Con esto si volvemos a ejecutar nuestra aplicación y pulsamos sobre los botones aparecerá el messagebox que corresponda:


Toolbar2


Usando Comandos



Este método conlleva un poco más de trabajo, pero es mucho más representativo de lo que una aplicación seria debería hacer. El primer paso es crear una clase nueva clase para cada botón:


comandos1


Como podéis ver hemos creado una clase para cada acción que queremos realizar: Client, Invoice y Update, estas clases deben implementar el Interface ICommand para que se puedan usar como comandos en nuestra aplicación:


'UpdateCommand.vb
Public Class UpdateCommand
Implements ICommand

Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
'Aquí código para comprobar si tu comando puede ejecutarse.
Return True
End Function

Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged

Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
MessageBox.Show("Click en actualizar mediante comandos")
End Sub
End Class

'ClientCommand.vb
Public Class ClientCommand
Implements ICommand

Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
'Aquí código para comprobar si tu comando puede ejecutarse.
Return True
End Function

Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged

Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
MessageBox.Show("Click en clientes mediante comandos")
End Sub
End Class

'InvoiceCommand.vb
Public Class InvoiceCommand
Implements ICommand

Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute
'Aquí código para comprobar si tu comando puede ejecutarse.
Return True
End Function

Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged

Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute
MessageBox.Show("Click en facturas mediante comandos")
End Sub
End Class


Los Comandos son muy útiles y una muy buena forma de distribuir el código de tu aplicación, te recomiendo que eches un vistazo a estos vídeos que explican mucho más a fondo el uso de comandos:


How Do I: Use Command Binding in WPF


How Do I: Create a Custom Command in WPF


Lo siguiente que necesitamos hacer es tener un lugar común desde el que exponer nuestros comandos, para esto podemos usar el ViewModel, añade un nuevo archivo de clase a tu proyecto, llámalo ViewModel y ponle el siguiente código:


Public Class ViewModel
Private _UpdateCommand As UpdateCommand = New UpdateCommand()
Private _ClientCommand As ClientCommand = New ClientCommand()
Private _InvoiceCommand As InvoiceCommand = New InvoiceCommand()

Public ReadOnly Property UpdateCommand As ICommand
Get
Return _UpdateCommand
End Get
End Property

Public ReadOnly Property ClientCommand As ICommand
Get
Return _ClientCommand
End Get
End Property

Public ReadOnly Property InvoiceCommand As ICommand
Get
Return _InvoiceCommand
End Get
End Property
End Class


Nuestro ViewModel simplemente expone propiedades públicas del tipo de nuestros comandos hacia la capa de Visualización, el Xaml en este caso, por lo que solo debemos referenciar en nuestro Xaml este ViewModel en nuestro DataContext:


<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:WPF_JumpList_And_Thumbnail_Toolbars"
Title="MainWindow" Height="350" Width="525">

<Window.DataContext>
<viewModel:ViewModel></viewModel:ViewModel>
</Window.DataContext>

</Window>


Por último ya solo nos queda enlazar nuestros botones con sus respectivos comandos, para esto usaremos la propiedad Command de cada ThumbButtonInfo:


<ThumbButtonInfo x:Name="btnUpdate"
ImageSource="{StaticResource IconUpdate}"
Description="Comprueba actualizaciones"
IsBackgroundVisible="True"
Command="{Binding UpdateCommand}">
</ThumbButtonInfo>
<ThumbButtonInfo x:Name="btnClient"
ImageSource="{StaticResource IconClient}"
Description="Ver los clientes"
IsBackgroundVisible="True"
Command="{Binding ClientCommand}">
</ThumbButtonInfo>
<ThumbButtonInfo x:Name="btnInvoice"
ImageSource="{StaticResource IconInvoice}"
Description="Ver las facturas"
IsBackgroundVisible="True"
Command="{Binding InvoiceCommnad}">
</ThumbButtonInfo>


Como podéis ver simplemente hacemos un Binding a nuestro comando. Si ejecutáis la aplicación de nuevo y presionáis sobre un botón veréis que el efecto es el mismo que con los eventos, solo que en esta ocasión nuestro código es mucho más reutilizable y está más ordenado, pudiendo compartir entre varias ventanas o controles el mismo comando y conservando la misma funcionalidad:


comandos2





Y esto es todo. Tras esta serie de artículos ya sabéis todo lo necesario para hacer a vuestra aplicación más "Windows7 Friendly” y, lo más importante, más intuitiva, sencilla y útil para vuestros usuarios.


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