Este artigo apresenta o desenvolvimento de aplicações .NET voltadas para a plataforma Windows Forms (mais conhecida como Desktop). São trabalhados neste artigo a criação de uma janela principal, com um menu, barra de tarefas e barra de status, layouts MDI vs. SDI, construção de barras de menu, tarefas e status.

Aplicativos desktop são a maioria no ambiente Windows, muito utilizados na área comercial e automação. Dessa forma, este artigo serve para introduzir o leitor no uso da tecnologia através da criação de um exemplo simples.


Guia do artigo:

Este artigo é focado para o público iniciante em aplicações Windows Forms com o .NET. Traz informações úteis para quem está aderindo a esta plataforma e precisa saber quais devem ser os componentes e suas propriedades envolvidas na criação de uma aplicação.

A leitura deste artigo traz subsídios básicos para a criação de uma aplicação desktop, fazendo uso de Windows Forms, com uso de menus, barra de tarefas e barra de status. Também são apresentados conceitos e demonstrações de aplicações SDI e MDI fazendo uso de janelas Modal e não Modal.

Aplicações Desktop, conhecidas até pouco tempo como aplicações Win32, dominaram o mercado de automação comercial. Aplicações desenvolvidas com Visual Basic ou Delphi ofereciam (e oferecem) ao usuário enormes recursos para realização de seus processos, de forma fácil, rápida e, porque não dizer bela, com telas muito bem elaboradas. Com a chegada do Windows Vista, os desenvolvedores (os mais antigos claro) começam a reviver algo que passaram há uma década, quando manter uma aplicação D.O.S. executando em um ambiente Windows começava a ficar cada vez mais difícil. Até quando aplicações Win32 executarão perfeitamente em um ambiente Windows atualizado? A plataforma .NET é, sem dúvida alguma, a melhor opção, também, para ambientes desktop, através de suas aplicações Windows, com seus Windows Forms.

Note que desenvolver uma aplicação com Windows Forms, não significa dizer que ela é exclusivamente para Desktop, ela pode ser apenas uma interface que trabalha através de Web Services para recuperação e manutenção de dados. Quanto à linguagem, é possível escolher Visual Basic, Delphi ou C#, dentre outras. É claro que há uma mudança, de certa forma radical, de paradigma, principalmente no que diz respeito ao acesso a dados. Este artigo apresenta uma introdução ao desenvolvimento de uma aplicação desktop, trazendo os componentes necessários para a criação de uma janela principal para uma aplicação, assim como diferenciação entre interfaces SDI e MDI para aplicações com janelas.

Aplicações MDI, SDI e TBI

MDI (Multiple Document Interface) é uma característica de aplicações que possuem suas janelas sempre “amarradas” a uma janela principal. Esta janela principal também é conhecida como MDI Parent, MDI Container, Parent Window ou Parent Form. Em português encontram-se traduções na literatura como Janela Pai ou Proprietária. SDI (Simple Document Interface) já tem uma característica diferenciada, onde toda janela é independente, não ficando limitada a sua exibição à área cliente disponibilizada pela Parent Window. Já o TBI (Tabbed Document Interface) é uma característica que surgiu nos últimos anos, através dos browsers, que começa a ser utilizada em aplicações desktop também. As imagens abaixo apresentam exemplos das características MDI, SDI e TBI.

MDI
MDI
SDI
SDI
TDI
TDI

Antes de apresentar a implementação de aplicações SDI ou MDI, será trabalhada a criação da aplicação em si, mas é importante ter estes conceitos em mente.

Criando e configurando o projeto

Acesse o Visual Studio e crie uma nova solução selecionando os menus File > New Project > Other Project Types > Visual Studio Solutions e escolha a opção Blank Solution. Uma solução pode conter nela vários projetos. Pode-se pensar em ter uma solução com um projeto para a camada de apresentação, um para a camada de negócios e um para a camada de persistência por exemplo. No Solution Explorer (Ctrl-Alt-L) clique com o botão direito do mouse sobre o nome da solução criada.

Caso a solução não apareça selecione os menus Tools > Options > Projects and Solutions e marque a opção Always show solution.

No menu popup exibido escolha a opção Add > New Project. Na linguagem Visual C# escolha a opção Windows e então Windows Forms Application. O projeto é criado com um formulário vazio (Form1.cs). Elimine-o, pressionando a tecla Del com o arquivo selecionado.

Criando uma janela principal para a aplicação

A maioria das aplicações possui uma janela, onde as opções da mesma são exibidas, por menus ou barras de ferramentas. Desta forma, vamos criar uma janela que cumpra este objetivo. Clique novamente com o botão direito do mouse, agora sobre o nome do projeto e escolha a opção Add > Windows Form e dê a este novo formulário o nome FormJanelaPrincipal.cs. Será preciso configurar algumas propriedades para este formulário. Tenha certeza de estar com o formulário selecionado e pressione F4 para visualizar a guia Properties. Siga a Tabela 1. Após estas configurações, abra o arquivo Program.cs no Solution Explorer e altere o nome Form1 para FormJanelaPrincipal. Salve seu projeto e pressione F5 para execução de sua aplicação. Seu formulário deverá ser exibido ocupando toda a área de trabalho, não deverá permitir mudar seu tamanho, estará exibindo o ícone escolhido ao lado esquerdo da barra de títulos e também o título informado.

Tabela 1. Configuração do formulário principal
Propriedade Objetivo Valor
(Name) Nomear o formulário. Propriedade comum para todos os controles. FormJanelaPrincipal
FormBorderStyle Define o tipo de borda para o formulário. Fixed3D
Icon Definição de um ícone para o formulário. Escolha um ícone, caso deseje.
Text Define o texto a ser exibido na barra de títulos do formulário. Aplicação .NET Magazine
MaximizeBox Define se o Box com o ícone de maximizar será exibido ou não no formulário. False
WindowState Define o estado padrão de visualização para o formulário. Maximized

Criando menus, barra de tarefas e de status

Uma vez criado e configurado o formulário responsável pela janela principal, é preciso inserir neste formulário os meios de acesso às opções da aplicação. Na Toolbox (Ctrl+Alt+X), na categoria Menus & Toolbars selecione e arraste para o formulário o componente ToolStripContainer e você terá em sua janela algo semelhante ao apresentado pela Figura 1.

Inserindo um ToolStripContainer
Figura 1. Inserindo um ToolStripContainer

O ToolStripContainer é um componente composto de cinco painéis que podem ser utilizados também como containers, nas bordas são dispostos instâncias de ToolStripPanel e na área central um ToolStripContentPanel. A ideia é que nas bordas sejam inseridos menus, barras de tarefas e barras de mensagens, compondo assim uma janela padrão de aplicações desktop. O ToolStripContainer permite que os painéis das bordas aceitem docking, ou seja, que controles possam ser postos neles.

A visibilidade dos painéis por default é ativa, como mostra o popup da SmartTag da Figura 1. É possível alterar estes valores também por código e na janela de propriedades (F4) através das propriedades TopToolStripPanelVisible, BottomToolStripPanelVisible, LefToolStripPanelVisible e RightToolStripPanelVisible.

A área central do ToolStripContentPanel tem o objetivo de receber o conteúdo a ser apresentado ao usuário, desta forma, a ideia é que este painel ocupe realmente toda a área disponibilizada pela janela, desta forma, clique no link Dock Fill in Form que aparece na SmartTag ou configure a propriedade Dock dele para Fill, como mostra a Figura 2. Após estas configurações feitas sua janela deve estar semelhante à apresentada pela Figura 3.

SmartTag (ou Tasks), vista na Figura 2, é uma janela suspensa que o VS apresenta com opções comuns para configuração de um controle.

Configurando o dock do ToolStripContainer
Figura 2. Configurando o dock do ToolStripContainer
ToolStripContainer ocupando toda a área da janela
Figura 3. ToolStripContainer ocupando toda a área da janela

É possível verificar através do apresentado pela Figura 3 que nem todas as bordas têm os painéis expandidos, mas isso é facilmente resolvido clicando no botão que existe no centro de cada borda. Clicando neste botão ele recolhe e expande o painel.

É preciso agora adicionar ao ToolStripContainer um menu, uma barra de tarefas e uma barra de mensagem/status. Isso é possível através dos componentes: MenuStrip, ToolStrip e StatusStrip respectivamente. Estes componentes estão na mesma categoria que o ToolStripContainer na ToolBox. Arraste primeiro o MenuStrip para o TopStripPanel, depois, para o mesmo Panel, arraste o ToolStrip e para finalizar, arraste para o BottomStripPanel o StatusStrip.

Vamos agora configurar os controles inseridos no ToolStripContainer, começando pelo MenuStrip. Ao clicar no componente MenuStrip é exibido um container com o texto Type Here. Neste local é possível inserir um tipo de item ao menu, como ToolStripMenuItem (que é o padrão para os menus), ComboBox ou um TextBox. Usaremos o ToolStripMenuItem. Para isso, basta digitar o título do menu na caixa em que é solicitado. Digite “Cadastros” e note que é aberto abaixo do novo item de menu campos para menus suspensos. Crie um item do menu suspenso chamado “Clientes” e outro “Fornecedores”. Ao lado do menu Cadastros crie um outro chamado “Relatórios” e abaixo dele “Contas a Receber” e “Contas Pagar”.

Para inserir botões a sua barra de tarefas clique na mesma e será exibido a você um ComboBox. Expandindo este componente é exibida uma relação de vários componentes possíveis de serem inseridos em sua barra de tarefa. Vale a pena testar cada um deles, porém nos concentraremos no Button. Insira um Button em sua barra de tarefas e veja na Tabela 2 as propriedades a serem configuradas.

Tabela 2. Configuração do Button da barra de tarefas
Propriedade Objetivo Valor
DisplayStyle Permite definir o que é exibido no Button, imagem, texto, imagem e texto ou nada Image
Image Define a imagem que será exibida. Ao clicar no botão à direita do valor desta propriedade uma janela será exibida, para que, através dela, a imagem possa ser selecionada. A imagem selecionada pode ser um recurso (resource) existente e disponível no projeto. Resources não fazem parte do escopo deste artigo. Como não existem recursos disponíveis, a imagem precisa ser importada. Qualquer imagem jpg, jpeg, png, gif, wmf ou bmp
ImageAlign Definição da posição da imagem no Button, quando se atribui ImageAndText para DisplayStyle. MiddleCenter
ToolTipText Definição de um texto de ajuda para ser exibido ao usuário quando o cursor do mouse passar por sobre o Button. Acesso ao cadastro de clientes

Insira na barra de tarefas outro Button, agora com o ToolTipText “Acesso ao relatório de contas a pagar”.

Para inserir um componente à barra de status o processo é o mesmo utilizado para a inserção na barra de tarefas. A variação de componentes possíveis é um pouco inferior, mas a investigação é igualmente interessante. Neste exemplo será feito uso do StatusLabel. As propriedades a serem configuradas para este componente são apenas duas: (Name) com o valor lblStatus e Text que deve ficar vazia. Neste momento sua tela deve estar semelhante à apresentada na Figura 4.

Janela principal com strips inseridos e configurados
Figura 4. Janela principal com strips inseridos e configurados

Clique na barra de tarefas, sobre a linha de pontos vertical e arraste a barra de tarefas para as laterais da janela, veja que elas acabam se fixando nas laterais, sem que você tenha tido a necessidade de escrever nenhuma linha de código. Este comportamento, porém, pode não ser o desejado por você. Para proibir este tipo de comportamento, clique na Smart Tag da ToolStrip e atribua o valor Hidden para GripStyle, como mostra a Figura 5.

Desabilitando o Grip
Figura 5. Desabilitando o Grip

Criando novos formulários

Crie em seu projeto um novo formulário, da mesma forma que fizemos anteriormente, dê a ele o nome FormClientesCRUD.cs, configure o formulário de acordo a Tabela 3.

Tabela 3. Configuração do formulário FormClientesCRUD
Propriedade Objetivo Valor
MaximizeBox Habilitação/Desabilitação do botão responsável pela maximização do formulário. False
StartPosition Define a posição inicial para a exibição do formulário. CenterScreen
Text Permite a definição do texto a ser exibido na barra de título do formulário. Manutenção em dados de Clientes

Com as mesmas configurações apresentadas na Tabela 3, crie agora outro formulário, chamado FormFornecedoresCRUD.cs, alterando apenas o valor da propriedade Text, mudando a palavra Clientes para Fornecedores.

Implementando o acesso aos formulários através da janela principal

Existem dois tipos de janelas quando se trabalha com aplicações desktop, as janelas do tipo Modal e as não modais (Modalless). As modais são aquelas em que o acesso às janelas que estejam atrás delas é “negado”, até que esta janela seja fechada. Já as janelas Modalless são janelas que podem ser exibidas e a ativação de uma delas pode ser feita sem a necessidade de se fechar a que esteja ativa no momento.

No formulário principal, acesse o item de menu Clientes, dentro de Cadastros e na janela de Propriedades, na guia Events, dê duplo clique na caixa em branco ao lado do evento Click. No editor de códigos, digite o código apresentado pela Listagem 1. Siga o mesmo procedimento para o acesso à Janela de Fornecedores (o código é visto na Listagem 2).

Listagem 1. Evento Click do menu de acesso ao cadastro de clientes

        private void clientesToolStripMenuItem_Click(object sender, EventArgs e)
        {
        FormClientesCRUD formClientesCRUD = new FormClientesCRUD();
        formClientesCRUD.Show();
        }
        
Listagem 2. Evento Click do menu de acesso ao cadastro de fornecedores

        private void fornecedoresToolStripMenuItem_Click(object sender, EventArgs e)
        {
        FormFornecedoresCRUD formFornecedoresCRUD = new FormFornecedoresCRUD();
        formFornecedoresCRUD.Show();
        }
        

Um evento acontece quando o sistema precisa responder a uma ação do usuário em algum controle de tela. Por exemplo, o evento Click é disparado quando o usuário clica em um botão, item de menu, formulário etc. A “resposta” a um evento é expressa em código, que na verdade na linguagem C# do .NET se chama método. Podemos dizer, por exemplo, que o método fornecedoresToolStripMenuItem_Click é a resposta ao evento Click do item de menu relativo ao cadastro de fornecedores.

Teste sua aplicação (pressione CTRL + F5) e acesse os formulários através dos menus. Veja que mesmo com uma janela aberta é possível acessar o menu na janela principal e acessar uma outra janela. Caso você acesse a mesma janela é preciso saber que da forma que o código está implementado, o formulário será instanciado a cada vez que se deseja acessar e isso pode ser custoso. Como este exemplo trabalha com janelas Modalless é interessante alterar esta implementação. O primeiro passo é declarar uma variável que represente a classe do formulário (um atributo à classe) e então, quando for acessar o formulário, verificar se a janela já não foi instanciada antes de chamá-la, como é apresentado pelo código implementado pela Listagem 3.

Listagem 3. Alteração na chamada a uma janela através do menu

        private FormClientesCRUD formClientesCRUD;

        private FormFornecedoresCRUD formFornecedoresCRUD;

        private void clientesToolStripMenuItem_Click(object sender, EventArgs e)
        {
        if (formClientesCRUD == null)
        formClientesCRUD = new FormClientesCRUD();
        formClientesCRUD.Show();
        }

        private void fornecedoresToolStripMenuItem_Click(object sender, EventArgs e)
        {
        if (formFornecedoresCRUD == null)
        formFornecedoresCRUD = new FormFornecedoresCRUD();
        formFornecedoresCRUD.Show();
        }
        

Há situações em que não seja permitido que o usuário acesse outra janela da aplicação enquanto não terminar uma atividade específica. Sendo assim é preciso proibir que ele acesse as janelas atrás da atual. Isso é muito simples, basta que ao invés de se chamar o método Show() seja chamado o método ShowDialog().

Mudando de SDI para MDI

No exemplo criado anteriormente, uma janela criada, como a de Clientes, é vista como uma janela independente. Faça um teste, altere no formulário da janela principal a propriedade WindowState para Normal, execute sua aplicação e chame as janelas criadas (Clientes e Fornecedores). Veja que as janelas são independentes e podem ser arrastadas para qualquer parte da área de trabalho (Desktop). Essa é a característica de aplicações SDI e isso em algumas situações pode não ser uma boa política. Desta forma, vamos alterar a aplicação para que faça uso dos recursos de uma aplicação MDI.

O primeiro ponto é saber que o controle ToolStripContainer não pode ser utilizado em aplicações MDI, desta forma os strips devem ser colocados diretamente no formulário e perde-se a capacidade de ancoragem dos strips nas laterais da página, mesmo visualizando o Grip nas strips. Retire o ToolStripContainer de sua janela principal ou crie uma outra. Para não perder as barras e menus criados, mova-os para fora do ToolStripContainer de modo que fiquem associados ao formulário.

Para o formulário principal, configure especificamente a propriedade da Tabela 4.

Tabela 4. Configuração do formulário principal para ser um MDI container
Propriedade Objetivo Valor
IsMdiContainer Define o formulário como MDI Container True

Note que a definição do formulário como um container MDI causa um efeito visual nele, que deixa a área cliente (área fora do menu, barra de tarefas e barra de status) em uma cor mais escura. A criação de uma aplicação com a característica MDI requer algumas preocupações a mais. Veja a nova implementação para a chamada ao formulário de clientes de acordo a Listagem 4. Para o formulário de fornecedores, proceda como a Listagem 5.

Listagem 4. Chamando uma janela MDI filha (clientes)

        private void clientesToolStripMenuItem_Click(object sender, EventArgs e)
        {
        FormClientesCRUD formClientesCRUD = new FormClientesCRUD();
        formClientesCRUD.MdiParent = this;
        formClientesCRUD.Show();
        (sender as ToolStripMenuItem).Enabled = false;
        }
        
Listagem 5. Chamando uma janela MDI filha (fornecedores)

        private void fornecedoresToolStripMenuItem_Click(object sender, EventArgs e)
        {
        FormFornecedoresCRUD formFornecedoresCRUD = new FormFornecedoresCRUD();
        formFornecedoresCRUD.MdiParent = this;
        formFornecedoresCRUD.Show();
        (sender as ToolStripMenuItem).Enabled = false;
        }
        

Observe a atribuição “formClientesCRUD.MdiParent = this” abaixo da criação do objeto. Esta atribuição define como janela pai do formulário criado o formulário atual, que neste caso é a janela principal. A última linha tem a responsabilidade de desabilitar o item do menu do respectivo evento, para que o usuário não possa criar novamente a janela antes que ela seja fechada. Execute sua aplicação e procure mover a janela de clientes dentro da principal e você verá que ela não sai dos limites da área cliente, ocultando a parte que não couber nos limites, como mostra a Figura 6.

Movendo uma janela filha em aplicação MDI
Figura 6. Movendo uma janela filha em aplicação MDI

Como você pode ter notado nas Listagens 4 e 5, foi usado um objeto chamado Sender. Ele na verdade é passado como parâmetro para o método que está respondendo ao evento. Ele representa o objeto que sofreu o evento, nesse caso, o próprio item de menu. Nesse caso, fazemos uma conversão dele para o tipo específico e configuramos sua propriedade Enabled para desabilitá-lo.

Veja que ambas as janelas ficam abertas e visíveis, permitindo a visualização de mais de uma janela ao mesmo tempo e isso é realmente muito bom.

Uma vez acessado um formulário, sua opção de menu é desabilitada, como visto pelo código das Listagens 4 e 5, desta forma, é preciso pensar em como tornar o menu ativo novamente e, isso pode ser feito no momento em que a janela é fechada, como mostra a implementação da Listagem 6.

Listagem 6. Habilitando menu para criação de janela

        private void FormClientesCRUD_FormClosed(object sender, FormClosedEventArgs e)
        {
        (MdiParent.MainMenuStrip.Items[0] as ToolStripMenuItem)
        .DropDownItems[0].Enabled = true;
        }
        

Veja que o código da Listagem 6 traz a implementação para o evento FormClosed do formulário de clientes. Este código obtém o primeiro menu do MainStripMenu definido na janela principal (MdiParent), que é o Cadastro e então é obtido o primeiro item de menu, que é Clientes e então este item é tornado habilitado novamente. É importante que a propriedade MainMenuStrip do formulário que representa a janela principal tenha atribuído a ela o menu principal.

Mas como chamar novamente uma janela que já foi aberta e teve sua opção de acesso via menu, desabilitada, sem ter que fechar esta janela para novamente habilitar o menu? O próximo item trata esta situação e algumas outras.

Criando um menu para as janelas ativas em uma aplicação MDI

Em algumas aplicações MDI é comum verificar como último menu na barra um menu chamado Janela ou Window. Neste menu aparece uma relação de todas as janelas ativas e opções para organizar as mesmas dentro da Janela MDI parent. Crie um novo menu ao lado do de Relatórios, dê a ele o nome mnuJanelas e na propriedade Text informe o valor Janelas (ou Window). Agora, selecione o MenuStrip e na propriedade MdiWindowListItem selecione este novo menu criado. Execute sua aplicação, abra os dois formulários de cadastro e clique no menu Window, ele deverá estar semelhante ao apresentado pela Figura 7.

Menu que lista todas as janelas abertas em uma
    aplicação MDI
Figura 7. Menu que lista todas as janelas abertas em uma aplicação MDI

Em aplicações MDI, onde várias janelas abertas ao mesmo tempo ficam todas dentro da região destinada a elas no Parent Form, é preciso poder organizar estas janelas. Inclua no menu Window (ou Janela) as opções Cascata, Lado a Lado e Fechar Janela Atual (veja que existe um separador antes da última opção, para inseri-lo, coloque um hífen na propriedade Text), como mostra a Figura 8.

Opções para organizar as janelas
Figura 8. Opções para organizar as janelas

A implementação para os itens criados no menu apresentado pela Figura 8 pode ser vista na Listagem 7. Para criar os manipuladores para os eventos mostrados, você pode simplesmente dar um duplo clique sobre os itens de menu, isso equivale a acionar a janela de propriedades e dar um duplo clique no lado do evento Click.

Listagem 7. Código para organização das janelas

        private void cascataToolStripMenuItem_Click(object sender, EventArgs e)
        {
        LayoutMdi(MdiLayout.Cascade);
        }

        private void ladoALadoToolStripMenuItem_Click(object sender, EventArgs e)
        {
        LayoutMdi(MdiLayout.TileVertical);
        }

        private void fecharToolStripMenuItem_Click(object sender, EventArgs e)
        {
        ActiveMdiChild.Close();
        }
        

Você pode configurar o Click dos botões da barra de tarefas para simplesmente apontarem para os mesmos métodos dos itens de menu, para não reescrever código. Para isso, basta selecionar o botão, ir até os eventos na janela de Propriedades e escolher na caixa suspensa do evento Click o respectivo método.

Execute sua aplicação, abra os dois formulários, vá no meu Window (ou Janela) e teste as novas opções. Existem outras possibilidades de layout, que certamente você notou ao digitar o texto.

Conclusão

O desenvolvimento de aplicações para o ambiente desktop ainda está longe de não ser útil, porém, muitos desenvolvedores desenvolvem este tipo de aplicativo em linguagens e ambientes voltadas ao Win32. É preciso começar a desenvolver aplicações Windows voltadas ao .NET e este artigo apresentou conceitos básicos que podem facilmente subsidiar um estudo aprofundado e desenvolvimento de aplicações de teste neste ambiente. Pouco código foi escrito neste artigo, o que em hipótese alguma garante que o desenvolvimento de aplicações corporativas seja sempre desta forma. Ninguém está livre da codificação, mas ela não é difícil. Procure investigar os pontos sugeridos neste artigo e termino colocando-me à inteira disposição para auxílio em dúvidas que eu possa ajudar.

A escolha da característica (MDI ou SDI) para a aplicação a ser desenvolvida também é importante e deve ser feita antes do início da implementação, pois como visto neste artigo, alguns controles funcionam bem SDI, mas não dão suporte ao MDI e uma mudança com o projeto andando, pode ser drástica.

Confira também