André Alves de Lima

Talking about Software Development and more…

Mudando o ícone do aplicativo em tempo de execução

Quando desenvolvemos aplicativos de negócios, algumas vezes o mesmo executável pode ser utilizado para acessar diferentes módulos, e o workflow utilizado é especificado via argumento ou até mesmo em um arquivo de configuração. Nesses casos, é comum que queiramos utilizar ícones diferentes dependendo do workflow que está sendo utilizado.

Alterar o ícone em tempo de execução é uma tarefa muito simples, desde que não estejamos trabalhando com atalhos. Ao trabalharmos com atalhos, o Windows não utiliza o ícone que escolhemos em tempo de execução na barra de tarefas, mas sim, o ícone do próprio atalho. Nesse artigo vou mostrar como utilizar a API do Windows para adicionar um overlay no ícone, de forma que pelo menos consigamos distinguir o ícone na barra de tarefas dependendo do workflow que está sendo executado.

Para entendermos o que estou querendo dizer, vamos supor que temos um aplicativo que tenha dois workflows: A e B. Se passarmos “A” como argumento na linha de comando, o workflow “A” será executado. Se passarmos “B” como argumento na linha de comando, o workflow “B” será executado. Para mantermos as coisas bem simples e focadas, a única diferença entre os dois workflows é que um escreverá “A” em um label na janela principal e outro escreverá “B”.

Crie um aplicativo do tipo Windows Forms Application e, na tela principal, adicione um label chamado “labelWorkflow“. No code-behind do formulário, vamos fazer um overload do método “OnShown” para indicarmos qual workflow está sendo executado, dependendo do que foi passado como argumento na linha de comando:

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            string[] args = Environment.GetCommandLineArgs();
            if (args.Contains("A"))
            {
                labelWorkflow.Text = "Workflow: A";
            }
            else if (args.Contains("B"))
            {
                labelWorkflow.Text = "Workflow: B";
            }
        }

Para testar o comportamento através do Visual Studio, você pode adicionar o argumento a ser utilizado na seção “Debug” da janela de propriedades do projeto:

Mudando o ícone em tempo de execução

Até agora nós simplesmente mudamos o label que indica o workflow em execução. E se quisermos alterar também o ícone do aplicativo, dependendo do que foi passado como argumento na linha de comando? Aparentemente é muito simples, desde que não estejamos utilizando atalhos para acessar a nossa aplicação. Antes de continuar, vou disponibilizar alguns ícones que eu utilizei nesse exemplo, de forma que você consiga reproduzir exatamente o efeito que estou demonstrando. Baixe os ícones através destes links: A.ico, B.ico, Wrench.ico, WrenchA.ico, WrenchB.ico. (A propósito, eu baixei esses ícones gratuitamente no site flaticon.com, e converti-os de png para ico utilizando o site convertico.com – excelentes ferramentas, recomendo).

Para alterarmos o ícone do aplicativo, temos que alterar o ícone da janela principal. Como a nossa aplicação de exemplo só tem um formulário, é justamente o ícone desse formulário que temos que alterar. O ícone do formulário pode ser alterado através de propriedade Icon:

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            string[] args = Environment.GetCommandLineArgs();
            if (args.Contains("A"))
            {
                labelWorkflow.Text = "Workflow: A";
                this.Icon = new Icon(@"c:\icons\wrenchA.ico");
            }
            else if (args.Contains("B"))
            {
                labelWorkflow.Text = "Workflow: B";
                this.Icon = new Icon(@"c:\icons\wrenchB.ico");
            }
        }

Ao executarmos a aplicação pelo Visual Studio passando “A” na linha de comando, temos o resultado esperado, com o ícone alterado tanto na barra de título do aplicativo quanto na barra de tarefas do Windows:

Porém, você sabe o que o Windows faz se criarmos um atalho para o aplicativo? Vamos verificar. Vá até a pasta bin/debug onde o aplicativo foi compilado, crie um atalho para o executável e adicione o argumento “A” no atalho:

Execute o aplicativo utilizando esse atalho e veja o resultado:

Percebeu que o ícone na barra de título foi alterado, mas, o ícone na barra de tarefas permaneceu o mesmo? Pois esse é o (péssimo) comportamento que a Microsoft escolheu para aplicativos sendo rodados através de um atalho: o ícone exibido na barra de tarefas será sempre o ícone configurado no atalho, independente se a aplicação realiza uma alteração do ícone em tempo de execução. E aí, como saímos dessa?

Mudando o ícone da barra de tarefas ao utilizarmos atalhos

Não existe uma maneira 100% ideal para resolvermos esse problema. O que temos à nossa disponibilidade é a possibilidade de adicionarmos um overlay no ícone da barra de tarefas. Um overlay nada mais é que uma pequena imagem de 16×16 pixels que será colocada sobre o ícone no canto inferior direito. Você já deve ter visto esse efeito no ícone do finado Live Messenger (que exibia um overlay representando o seu status: online, ocupado, ausente), nas versões anteriores do Skype (que também mostravam um overlay com o seu status), no Outlook (que mostra um overlay indicando se você tem e-mails novos) e em outros aplicativos de mensagens e e-mails (que, em alguns casos, chegam a mostrar o número de mensagens não lidas no overlay).

Vamos, então, adicionar um overlay ao ícone da nossa aplicação (um pequeno “A” ou “B” dependendo do argumento passado). Para isso, como estamos trabalhando com um exemplo em Windows Forms, precisamos de uma referência ao Windows API Code Pack. Antigamente a Microsoft disponibilizava esse code pack para download, mas, não sei por que ela acabou retirando. Felizmente, algumas pessoas salvaram essa biblioteca e até mesmo disponibilizaram como pacote do NuGet. Então, instale o pacote chamado “Windows API Code Pack 1.1” (Id “WindowsAPICodePack” – já escrevi anteriormente sobre o gerenciamento de pacotes NuGet no Visual Studio caso você tenha dúvidas quanto a essa tarefa).

Agora, ao invés de alterarmos o ícone do aplicativo, vamos adicionar o overlay. Essa tarefa é muito simples com o Windows API Code Pack. Basta chamarmos o método SetOverlayIcon da classe TaskBarManager passando o ícone para o overlay e opcionalmente um texto para acessibilidade:

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            string[] args = Environment.GetCommandLineArgs();
            if (args.Contains("A"))
            {
                labelWorkflow.Text = "Workflow: A";
                Microsoft.WindowsAPICodePack.Taskbar.TaskbarManager.Instance.SetOverlayIcon(new System.Drawing.Icon(@"c:\icons\a.ico"), string.Empty);
            }
            else if (args.Contains("B"))
            {
                labelWorkflow.Text = "Workflow: B";
                Microsoft.WindowsAPICodePack.Taskbar.TaskbarManager.Instance.SetOverlayIcon(new System.Drawing.Icon(@"c:\icons\b.ico"), string.Empty);
            }
        }

E qual o resultado dessa alteração?

Agora tivemos o resultado inverso. O ícone foi alterado na barra de tarefas e permaneceu o mesmo na barra de título do aplicativo. Você terá que escolher entre alterar o ícone da barra de título ou alterar o ícone da barra de tarefas. Não dá para alterar os dois (até dá, mas, o resultado será que o overlay ficará por cima do ícone já alterado na barra de tarefas, produzindo um resultado muito estranho). Eu acredito que a alteração do ícone na barra de tarefas é mais importante, uma vez que o ícone é bem maior e é essencial diferenciá-lo, principalmente se mais de uma instância da aplicação tiver que ser executada ao mesmo tempo.

No WPF é bem mais simples

A diferença no WPF é que não precisamos utilizar uma biblioteca externa, uma vez que temos à nossa disponibilidade a API da Taskbar de forma nativa. Para utilizá-la, temos que declarar no XAML da Window uma instância de TaskbarItemInfo:

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Icon="wrench.ico">
    <Window.TaskbarItemInfo>
        <TaskbarItemInfo />
    </Window.TaskbarItemInfo>
    <Grid>
    </Grid>
</Window>

E depois é só configurar a propriedade Overlay apontando para o ícone desejado:

            string[] args = Environment.GetCommandLineArgs();
            if (args.Contains("A"))
            {
                this.TaskbarItemInfo.Overlay = new BitmapImage(new Uri(@"c:\icons\a.ico"));
            }
            else if (args.Contains("B"))
            {
                this.TaskbarItemInfo.Overlay = new BitmapImage(new Uri(@"c:\icons\b.ico"));
            }

Simples, não?

Concluindo

Não sei porque a Microsoft decidiu que os ícones na barra de tarefas se comportam dessa maneira estranha ao utilizarmos atalhos. Mas, temos que trabalhar com o que nos é proporcionado. Nesse caso, a melhor saída para termos ícones diferentes na barra de tarefas é utilizando overlays. Nesse artigo você viu como configurar um overlay para o ícone da barra de tarefas tanto no Windows Forms quanto no WPF. Espero que essa dica seja útil para você em alguma situação no futuro. Caso positivo, compartilhe a sua experiência nos comentários!

Caso você queira ficar por dentro das novidades do meu site, assine a minha newsletter. Fazendo isso, você receberá, uma vez por semana, um pequeno texto sobre o artigo publicado, uma prévia do artigo da próxima semana, além de dicas exclusivas que eu só compartilho por e-mail. Assine aqui ou utilizando o formulário logo abaixo.

Até a próxima semana!

André Lima

Icons made by Freepik from www.flaticon.com is licensed by CC BY 3.0

Newsletter do André Lima

* indicates required



Powered by MailChimp

4 thoughts on “Mudando o ícone do aplicativo em tempo de execução

  • Accioly disse:

    Legal essa dica, parabéns André. Abraços

  • alexandre bisewski disse:

    Tem uma saída muito ruim mas q venho usando pois eu uso o DrawString e a cada novo segundo preciso de uma novo valor.

    Basta ao abrir o programa pelo atalho clicar sobre o ícone no taskbar e selecionar Fixar na Barra de Tarefas e depois o mesmo procedimento e é só clicar em Desafixar da Barra de Tarefas.

    Não sei pq mas funciona, tive esse problema desde q a API foi lançado.

    É uma dica ruim mas quebra galho.

    Abraços e parabéns pelo artigo.

    • andrealveslima disse:

      Olá Alexandre!

      Muito obrigado por ter deixado essa dica aqui nos comentários.. E não existe dica ruim.. Se funciona, está valendo..

      Grande abraço!
      André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *