29 01 2011
WPF – Criando Jumplists na Taskbar do Windows 7
[Atenção!!! Este artigo tem sua versão em vídeo! Se quiser pular a parte escrita e só assistir o vídeo, dê uma olhada no final do post!]
Olá pessoal, tudo certo?
No artigo / vídeo de hoje vou dar continuidade à série que mostra como interagir com a Taskbar do Windows 7 através de nossas aplicações WPF. O tema deste artigo especificamente é a criação de JumpLists na Taskbar.
Com certeza você já deve ter percebido que algumas aplicações apresentam alguns itens ao clicar no ícone delas na Taskbar do Windows 7. Por exemplo, o Microsoft Outlook mostra uma lista de tarefas que podem ser executadas dentro dele, como criar um novo e-mail, criar uma nova tarefa, etc. Já no caso do Windows Explorer, ele mostra os arquivos / caminhos mais frequentemente acessados.
JumpList do Microsoft Outlook
JumpList do Windows Explorer
Que tal se conseguíssemos criar itens personalizados para a nossa aplicação, com funcionalidades que fazem sentido para a nossa realidade? Pois isso obviamente é possível (e fácil)!
Iniciemos criando um projeto do tipo WPF Application no Visual Studio 2010, utilizando o .NET Framework 4. A nossa aplicação será um visualizador de arquivos texto bem simples, já que o objetivo principal do artigo é demonstrar a funcionalidade de criação de jumplists. Portanto, na MainWindow que é criada automaticamente no nosso novo projeto, vamos adicionar um Button para exibir uma janela e selecionarmos um arquivo texto e um FlowDocumentReader, para exibir o conteúdo do arquivo selecionado. O XAML e a interface devem ficar parecidos com o seguinte:
O próximo passo é implementarmos o código do click do nosso Button, que deverá exibir um diálogo para selecionarmos o arquivo texto a ser aberto e na sequência deverá exibir esse arquivo no nosso FlowDocumentReader. Para isso, vamos implementar no código C# da nossa Window o seguinte método:
public void ExibirArquivo(string caminho) { Paragraph paragraph = new Paragraph(); paragraph.Inlines.Add(System.IO.File.ReadAllText(caminho)); FlowDocument document = new FlowDocument(paragraph); fdrConteudoArquivo.Document = document; }
Basicamente o método acima recebe o caminho de um arquivo e exibe o conteúdo desse arquivo no nosso FlowDocumentReader. Feito isso, podemos agora implementar o código do evento Click do nosso botão como abaixo:
private void btAbrir_Click(object sender, RoutedEventArgs e) { Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog(); dialog.Filter = "Text documents (.txt)|*.txt"; bool? result = dialog.ShowDialog(); if ((result != null) && ((bool)result)) ExibirArquivo(dialog.FileName); }
Com isso, se você executar a aplicação verá que já podemos selecionar o arquivo desejado clicando no botão e ele será exibido no FlowDocumentReader. Agora vem a parte de fazermos a interface com a jumplist. A ideia é mostrar na jumplist a lista de arquivos que foram abertos pelo usuário. Para fazer isso, vamos criar no nosso código C# o seguinte método:
private System.Windows.Shell.JumpList jumpList = new System.Windows.Shell.JumpList(); private void AdicionaNaJumpList(string caminho) { // Primeiro verificando se o item já está presente na JumpList (para evitar duplicidade). bool itemJaExiste = false; foreach (System.Windows.Shell.JumpItem jumpItem in jumpList.JumpItems) { if ((jumpItem is System.Windows.Shell.JumpTask) && (((System.Windows.Shell.JumpTask)jumpItem).Arguments.Equals(caminho))) { itemJaExiste = true; break; } } // Se o item ainda não existe na JumpList, vamos adicioná-lo. if (!itemJaExiste) { // Configurando a JumpList da nossa aplicação para que ela seja o nosso atributo jumpList. System.Windows.Shell.JumpList.SetJumpList(Application.Current, jumpList); // Criando uma nova JumpTask. System.Windows.Shell.JumpTask jumpTask = new System.Windows.Shell.JumpTask(); jumpTask.Title = System.IO.Path.GetFileNameWithoutExtension(caminho); jumpTask.ApplicationPath = "notepad.exe"; jumpTask.Arguments = caminho; // Incluindo a JumpTask na nossa JumpList. jumpList.JumpItems.Add(jumpTask); // Confirmando as alterações da nossa JumpList. jumpList.Apply(); } }
Finalmente, precisamos incluir a chamada desse código no nosso método que abre o arquivo:
public void ExibirArquivo(string caminho) { Paragraph paragraph = new Paragraph(); paragraph.Inlines.Add(System.IO.File.ReadAllText(caminho)); FlowDocument document = new FlowDocument(paragraph); fdrConteudoArquivo.Document = document; // Adicionando o item na JumpList. AdicionaNaJumpList(caminho); }
Execute a aplicação e note que toda vez que um arquivo é aberto o nome do arquivo é adicionado na JumpList:
Ao clicarmos em algum dos itens na jumplist o arquivo será aberto no notepad. Isso acontece pois informamos no ApplicationPath da JumpTask que a aplicação chamada será o notepad.exe e que o argumento passado para ele será o caminho para o arquivo.
Mas, e se quisermos que ao clicar no item a nossa aplicação abra o arquivo? Para isso precisamos primeiramente fazer com que nossa aplicação somente possa ser executada uma vez e que ela trate os argumentos recebidos. Isso não é tão trivial de ser feito em WPF, mas, para nossa sorte, alguém já passou por isso e construiu uma solução simples e que funciona muito bem. Para maiores informações, dê uma olhada neste link.
Seguindo os passos do link acima, vamos ajustar nossa aplicação para que seja executada somente uma vez e que trate o argumento recebido para que o arquivo seja exibido no FlowDocumentReader, caso uma instância da aplicação já esteja sendo executada.
A primeira coisa a fazer é adicionar o arquivo SingleInstance.cs no nosso projeto. Após isso, devemos adicionar a referência a System.Runtime.Remoting no nosso projeto.
Em seguida, devemos ajustar o nosso arquivo App.xaml.cs para que a classe App implemente a interface ISingleInstanceApp e faça os tratementos necessários para a segunda execução da nossa aplicação. No nosso caso, o arquivo App.xaml.cs deve ser alterado para o seguinte:
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Windows; namespace WpfApplication6 { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application, Microsoft.Shell.ISingleInstanceApp { private const string Unique = "IdentificadorUnicoDaSuaAplicacao"; [STAThread] // O método Main será executado quando ainda não houver outra instância do nosso aplicativo sendo executado. public static void Main(string[] args) { if (Microsoft.Shell.SingleInstance<App>.InitializeAsFirstInstance(Unique)) { var application = new App(); application.InitializeComponent(); application.Run(); // Tratando o caso em que o usuário clica em um item da jumplist e nossa aplicação não está aberta. if (args.Length > 1) ((MainWindow)application.MainWindow).ExibirArquivo(ConstroiCaminhoCompleto(args)); Microsoft.Shell.SingleInstance<App>.Cleanup(); } } private static string ConstroiCaminhoCompleto(IEnumerable<string> args) { string resultado = string.Empty; System.Text.StringBuilder caminhoCompleto = new System.Text.StringBuilder(); for (int contador = 1; contador < args.Count(); contador++) caminhoCompleto.Append(string.Format(" {0}", args.ElementAt(contador))); return caminhoCompleto.ToString(); } #region ISingleInstanceApp Members // Esse método será chamado toda vez que nossa aplicação já estiver sendo // executada e o usuário clicar em um dos itens da jumplist. public bool SignalExternalCommandLineArgs(IList<string> args) { // Nesse caso, basta notificar nossa MainWindow para que ela exiba o // arquivo escolhido no nosso FlowDocumentReader. ((MainWindow)this.MainWindow).ExibirArquivo(ConstroiCaminhoCompleto(args)); return true; } #endregion } }
Por fim, precisamos indicar nos itens que serão criados na nossa JumpList que não será o notepad.exe que deverá abrir o arquivo, mas sim a nossa aplicação. Para isso basta alterar a linha onde setamos o ApplicationPath da nossa JumpTask para o seguinte:
jumpTask.ApplicationPath = System.Reflection.Assembly.GetEntryAssembly().Location;
Pronto! Com isso já temos nossa JumpList totalmente funcional, abrindo os arquivos na própria aplicação. Existem algumas propriedades interessantes que podemos configurar nas nossas JumpTasks. Veja a lista dos principais abaixo:
Description: para configurar a ToolTip do item da JumpList;
IconResourcePath e IconResourceIndes: para informar qual será o ícone utilizado no item da JumpList;
CustomCategory: por padrão os itens são adicionados na categoria “Tasks” da JumpList. Para incluí-los em uma categoria customizada, é só informar uma string nessa propriedade.
Também podemos utilizar as seguintes propriedades / método do nosso objeto JumpList:
ShowRecentCategory: para habilitar a categoria de itens recentes (o padrão é desabilitado);
AddToRecentCategory(JumpItem): para adicionar um item na lista de itens recentes;
ShowFrequentCategory: para habilitar a categoria de itens frequentes (o padrão é desabilitado). O mais interessante aqui é que basta habilitar a funcionalidade e o Windows toma conta de mostrar os itens mais frequentes automaticamente pra gente!
Além de JumpTasks, podemos criar também JumpPaths, porém, não mostrarei como criá-los neste artigo já que a criação é exatamente semelhante à criação de JumpTasks, exceto que a única propriedade disponível é a Path, que indica o caminho do arquivo que deve ser aberto pela aplicação default daquela extensão. Seria o mesmo que clicarmos para abrir o arquivo no Windows Explorer.
Devemos lembrar também que os itens podem ser criados diretamente no XAML da nossa Application, mas, dessa forma os itens são estáticos, por isso, só demonstrei a criação de itens através do C#, o que possibilita a criação de itens em runtime.
É isso aí! Espero que vocês tenham gostado desse artigo e que comecem a adicionar funcionalidades na JumpList das suas aplicações!
Abaixo vocês podem assistir o vídeo que mostra um passo-a-passo do conteúdo apresentado no artigo.
[Observação: para assistir o vídeo em uma qualidade melhor, clique no botão HD do Media Player abaixo]
Até a próxima!
André Alves de Lima.
WPF – Notificando progresso na Taskbar do Windows 7 Certificação Microsoft 70-511: Lista de Materiais
[…] ATENÇÃO!!! ESTE BLOG MUDOU DE ENDEREÇO: http://www.andrealveslima.com.br VISITE ESTE MESMO POST NO NOVO ENDEREÇO AQUI […]
André,
Muito bom o artigo. Parabéns!
Ari
Valeu Ari! Muito obrigado!
Abraço!
André Alves de Lima.
Bom artigo André,
Eu criei um mainwindows1, com dois frames, e criei duas pages, e coloquei um botão na page1 e Estou querendo chamar a page2 no frame2 pelo botão, mas o frame2 está no mainwindows, mas a page1 não consegue enxergar o frame2 que está no main windows.
Pode me ajudar?
Olá Leandro.
Obrigado pelo comentário. Você pode ou expor o Frame2 da sua MainWindow (criando uma propriedade pública) ou talvez criar um método que seria responsável pelo carregamento de um elemento no Frame2 da sua MainWindow.
André Lima
Very good article André. Thank you for sharing.
Hi Gilberto,
Thanks for the comment!
Regards,
André Lima