7. Interfaces gráficas com C# e VS.NET
7.1. Noções básicas sobre interfaces gráficas
7.1.1. Um primeiro projeto
Vamos criar um primeiro projeto do tipo “Aplicativo Windows”:
![]() |
- [1]: criar um novo projeto
- [2]: do tipo “Aplicativo Windows”
- [3]: o nome do projeto não importa neste momento
- [4]: o projeto foi criado
![]() |
- [5]: salvando a solução atual
- [6]: nome do projeto
- [7]: pasta da solução
- [8]: nome da solução
- [9]: será criada uma pasta para a solução [Chap5]. Os projetos dessa solução ficarão em subpastas.
![]() |
- [10]: o projeto [01] na solução [Chap5]:
- [Program.cs] é a classe principal do projeto
- [Form1.cs] é o arquivo-fonte que irá gerenciar o comportamento da janela [11]
- [Form1.Designer.cs] é o arquivo-fonte que irá encapsular as informações sobre os componentes da janela [11]
- [11]: o arquivo [Form1.cs] no modo “design”
- [12]: o aplicativo gerado pode ser executado pressionando (Ctrl-F5). A janela [Form1] é exibida. É possível movê-la, redimensioná-la e fechá-la. Assim, temos os elementos básicos de uma janela gráfica.
A classe principal [Program.cs] é a seguinte:
using System;
using System.Windows.Forms;
namespace Chap5 {
static class Program {
/// <summary>
/// O principal ponto de entrada do aplicativo.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
- linha 2: os aplicativos com formulários utilizam o namespace System.Windows.Forms.
- linha 4: o namespace inicial foi renomeado para Chap5.
- linha 10: ao executar o projeto (Ctrl-F5), o método [Main] é executado.
- linhas 11-13: a classe Application pertence ao espaço de nomes System.Windows.Forms. Ela contém métodos estáticos para iniciar/encerrar aplicativos gráficos do Windows.
- linha 11: opcional — permite atribuir diferentes estilos visuais aos controles inseridos em um formulário
- linha 12: opcional — define o mecanismo de renderização dos textos dos controles: GDI+ (true), GDI (false)
- linha 13: a única linha indispensável do método [Main]: instancia a classe [Form1], que é a classe do formulário, e solicita que ela seja executada.
O arquivo-fonte [Form1.cs] é o seguinte:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
}
}
- linha 5: a classe Form1 deriva da classe [System.Windows.Forms.Form], que é a classe-pai de todas as janelas. A palavra-chave partial indica que a classe é parcial e que pode ser complementada por outros arquivos-fonte. É o caso aqui, onde a classe Form1 está distribuída em dois arquivos:
- [Form1.cs]: no qual se encontra o comportamento do formulário, notadamente seus manipuladores de eventos
- [Form1.Designer.cs]: no qual se encontram os componentes do formulário e suas propriedades. Esse arquivo tem a particularidade de ser regenerado sempre que o usuário modifica a janela no modo [conception].
- linhas 6-8: o construtor da classe Form1
- linha 7: chama o método InitializeComponent. Percebe-se que esse método não está presente em [Form1.cs]. Ele se encontra em [Form1.Designer.cs].
O arquivo-fonte [Form1.Designer.cs] é o seguinte:
namespace Chap5 {
partial class Form1 {
/// <summary>
/// Variável de designer obrigatória.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Limpe todos os recursos que estão sendo usados.
/// </summary>
/// <param name="disposing">true se os recursos gerenciados devem ser descartados; caso contrário, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#Código gerado pelo Windows Form Designer
/// <summary>
/// Método obrigatório para suporte ao Designer — não modifique
/// o conteúdo deste método com o editor de código.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(196, 98);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
}
}
- linha 2: trata-se, mais uma vez, da classe Form1. Observe-se que não é mais necessário repetir que ela deriva da classe Form.
- linhas 25-37: o método InitializeComponent chamado pelo construtor da classe [Form1]. Esse método criará e inicializará todos os componentes do formulário. Ele é regenerado a cada alteração do formulário no modo [conception]. Uma seção, chamada région, é criada para delimitá-la nas linhas 19-39. O desenvolvedor não deve adicionar código nessa região: ele será sobrescrito na próxima regeneração.
É mais simples, inicialmente, não se preocupar com o código de [Form1.Designer.cs]. Ele é gerado automaticamente e representa a tradução para a linguagem C# das escolhas que o desenvolvedor faz no modo [conception]. Vejamos um primeiro exemplo:
![]() |
- [1]: selecione o modo [conception] clicando duas vezes no arquivo [Form1.cs]
- [2]: clicar com o botão direito do mouse no formulário e selecionar [Properties]
- [3]: a janela de propriedades do [Form1]
- [4]: a propriedade [Text] representa o título da janela
- [5]: a alteração da propriedade [Text] é refletida no modo [conception], bem como no código-fonte [Form1.Designer.cs]:
private void InitializeComponent() {
this.SuspendLayout();
...
this.Text = "Mon 1er formulaire";
...
}
7.1.2. Um segundo projeto
7.1.2.1. O formulário
Iniciamos um novo projeto chamado 02. Para isso, seguimos o procedimento explicado anteriormente para criar um projeto. A janela a ser criada é a seguinte:
![]() |
Os componentes do formulário são os seguintes:
n.º | nome | tipo | função |
1 | labelSaisie | Rótulo | uma descrição |
2 | textBoxSaisie | TextBox | um campo de preenchimento |
3 | buttonAfficher | Botão | para exibir o conteúdo do campo de entrada em uma caixa de diálogo textBoxSaisie |
Pode-se proceder da seguinte forma para criar essa janela:
![]() |
- [1]: clicar com o botão direito do mouse no formulário, fora de qualquer componente, e selecionar a opção [Properties]
- [2]: a janela de propriedades da janela aparece no canto inferior direito do Visual Studio
Entre as propriedades do formulário, destaque para:
para definir a cor de fundo da janela | |
para definir a cor dos desenhos ou do texto na janela | |
para associar um menu à janela | |
para atribuir um título à janela | |
para definir o tipo de janela | |
para definir a fonte das letras na janela | |
para definir o nome da janela |
Aqui, definimos as propriedades Text e Name:
Campos de entrada e botões - 1 | |
frmSaisiesBoutons |
![]() |
- [1]: selecione a caixa de ferramentas [Common Controls] entre as caixas de ferramentas oferecidas pelo Visual Studio
- [2, 3, 4]: clicar duas vezes, sucessivamente, nos componentes [Label], [Button] e [TextBox]
- [5]: os três componentes estão no formulário
Para alinhar e dimensionar corretamente os componentes, é possível usar os elementos da barra de ferramentas:
![]() | |||||||||
O princípio da formatação é o seguinte:
- selecione os diferentes componentes a serem formatados em conjunto (mantenha a tecla Ctrl pressionada enquanto clica para selecionar os componentes)
- selecione o tipo de formatação desejado:
- (continuação)
- as opções Align permitem alinhar os componentes pela parte superior, inferior, esquerda ou direita, ou pelo centro
- as opções “Make Same Size” permitem que os componentes tenham a mesma altura ou a mesma largura
- a opção “Horizontal Spacing” permite alinhar componentes horizontalmente com intervalos entre eles de mesma largura. O mesmo vale para a opção “Vertical Spacing”, que permite o alinhamento vertical.
- A opção Center permite centralizar um componente horizontalmente (Horizontally) ou verticalmente (Vertically) na janela
Depois de posicionados os componentes, definimos suas propriedades. Para isso, clique com o botão direito do mouse no componente e selecione a opção Properties:
![]() |
- [1]: selecione o componente para abrir sua janela de propriedades. Nela, altere as seguintes propriedades: name: labelSaisie, text: Saisie
- [2]: proceda da mesma forma: nome: textBoxSaisie, texto: não preencha nada
- [3]: name: buttonAfficher, text: Afficher
- [4]: a própria janela: nome: frmSaisiesBoutons, texto: Campos de entrada e botões - 1
- [5]: execute (Ctrl-F5) o projeto para ter uma primeira prévia da janela em ação.
O que foi feito no modo [conception] foi traduzido para o código de [Form1.Designer.cs]:
namespace Chap5 {
partial class frmSaisiesBoutons {
...
private System.ComponentModel.IContainer components = null;
...
private void InitializeComponent() {
this.labelSaisie = new System.Windows.Forms.Label();
this.buttonAfficher = new System.Windows.Forms.Button();
this.textBoxSaisie = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// labelSaisie
//
this.labelSaisie.AutoSize = true;
this.labelSaisie.Location = new System.Drawing.Point(12, 19);
this.labelSaisie.Name = "labelSaisie";
this.labelSaisie.Size = new System.Drawing.Size(35, 13);
this.labelSaisie.TabIndex = 0;
this.labelSaisie.Text = "Saisie";
//
// buttonAfficher
//
this.buttonAfficher.Location = new System.Drawing.Point(80, 49);
this.buttonAfficher.Name = "buttonAfficher";
this.buttonAfficher.Size = new System.Drawing.Size(75, 23);
this.buttonAfficher.TabIndex = 1;
this.buttonAfficher.Text = "Afficher";
this.buttonAfficher.UseVisualStyleBackColor = true;
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
//
// textBoxSaisie
//
this.textBoxSaisie.Location = new System.Drawing.Point(80, 19);
this.textBoxSaisie.Name = "textBoxSaisie";
this.textBoxSaisie.Size = new System.Drawing.Size(100, 20);
this.textBoxSaisie.TabIndex = 2;
//
// frmSaisiesBoutons
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 118);
this.Controls.Add(this.textBoxSaisie);
this.Controls.Add(this.buttonAfficher);
this.Controls.Add(this.labelSaisie);
this.Name = "frmSaisiesBoutons";
this.Text = "Saisies et boutons - 1";
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.Label labelSaisie;
private System.Windows.Forms.Button buttonAfficher;
private System.Windows.Forms.TextBox textBoxSaisie;
}
}
- linhas 53-55: os três componentes deram origem a três campos privados da classe [Form1]. Observe-se que os nomes desses campos são os nomes atribuídos aos componentes no modo [conception]. O mesmo se aplica ao formulário da linha 2, que é a própria classe.
- linhas 7-9: os três objetos do tipo [Label], [TextBox] e [Button] são criados. É por meio deles que os componentes visuais são gerenciados.
- linhas 14-19: configuração do rótulo labelSaisie
- linhas 23-29: configuração do botão buttonAfficher
- linhas 33-36: configuração do campo de entrada textBoxSaisie
- linhas 40-47: configuração do formulário frmSaisiesBoutons. Observe, nas linhas 43-45, como adicionar componentes ao formulário.
Esse código é compreensível. Assim, é possível criar formulários por meio de código sem utilizar o modo [conception]. Muitos exemplos disso são apresentados na documentação MSDN do Visual Studio. Dominar esse código permite criar formulários em tempo de execução: por exemplo, criar dinamicamente um formulário que permita a atualização de uma tabela de banco de dados, sendo que a estrutura dessa tabela só é identificada na hora da execução.
Resta-nos escrever o procedimento para gerenciar um clique no botão Afficher. Selecione o botão para acessar sua janela de propriedades. Ela possui várias abas:
![]() |
- [1]: lista de propriedades em ordem alfabética
- [2]: eventos relacionados ao controle
As propriedades e eventos de um controle podem ser acessados por categorias ou em ordem alfabética:
- [3]: Propriedades ou eventos por categoria
- [4]: Propriedades ou eventos em ordem alfabética
A aba Events no modo Catégories para o botão buttonAfficher é a seguinte:
![]() |
- [1]: a coluna à esquerda da janela lista os eventos possíveis no botão. Um clique no botão corresponde ao evento Click.
- [2]: a coluna da direita contém o nome do procedimento chamado quando o evento correspondente ocorre.
- [3]: se clicar duas vezes na célula do evento Click, o sistema passa automaticamente para a janela de código para escrever o manipulador do evento Click no botão buttonAfficher:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class frmSaisiesBoutons : Form {
public frmSaisiesBoutons() {
InitializeComponent();
}
private void buttonAfficher_Click(object sender, EventArgs e) {
}
}
}
- linhas 10-12: a estrutura básica do manipulador do evento Click para o botão denominado buttonAfficher. Observe os seguintes pontos:
- o método é nomeado de acordo com o esquema nomDuComposant_NomEvénement
- o método é privado. Ele recebe dois parâmetros:
- sender: é o objeto que provocou o evento. Se o procedimento for executado após um clique no botão buttonAfficher, sender será igual a buttonAfficher. É possível imaginar que o procedimento buttonAfficher_Click seja executado a partir de outro procedimento. Este, então, teria toda a liberdade para definir como primeiro parâmetro o objeto sender de sua escolha.
- EventArgs: um objeto que contém informações sobre o evento. Para um evento Click, ele não contém nada. Para um evento relacionado aos movimentos do mouse, encontraremos nele as coordenadas (X, Y) do mouse.
- Não utilizaremos nenhum desses parâmetros aqui.
Escrever um manipulador de evento consiste em preencher o esboço de código anterior. Aqui, queremos exibir uma caixa de diálogo contendo, se o campo textBoxSaisie não estiver vazio, o conteúdo desse campo ([1]); caso contrário, uma mensagem de erro ([2]):
![]() |
O código para fazer isso poderia ser o seguinte:
private void buttonAfficher_Click(object sender, EventArgs e) {
// é exibido o texto que foi digitado no TextBox textboxSaisie
string texte = textBoxSaisie.Text.Trim();
if (texte.Length != 0) {
MessageBox.Show("Texte saisi= " + texte, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information);
} else {
MessageBox.Show("Saissez un texte...", "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
A classe MessageBox serve para exibir mensagens em uma janela. Utilizamos aqui o seguinte método Show:
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon);
com
a mensagem a ser exibida | |
o título da janela | |
os botões presentes na janela | |
o ícone presente na janela |
O parâmetro buttons pode assumir valores entre as seguintes constantes (prefixadas por MessageBoxButtons, conforme mostrado na linha 7) acima:
botões | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
O parâmetro icon pode assumir valores entre as seguintes constantes (prefixadas por MessageBoxIcon, conforme mostrado na linha 10) acima:
![]() | idem Parar | ||
idem Aviso | ![]() | ||
idem Asterisk | ![]() | ||
![]() | idem Hand | ||
![]() |
O método Show é um método estático que retorna um resultado do tipo [System.Windows.Forms.DialogResult], que é uma enumeração:

Para saber em qual botão o usuário clicou para fechar a janela do tipo MessageBox, escreveremos:
7.1.2.2. O código relacionado ao gerenciamento de eventos
Além da função buttonAfficher_Click que escrevemos, o Visual Studio gerou, no método InitializeComponents de [Form1.Designer.cs] — que cria e inicializa os componentes do formulário —, a seguinte linha:
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
Click é um evento da classe Button [1, 2, 3]:
![]() |
- [5]: a declaração do evento [Control.Click] [4]. Assim, vemos que o evento Click não é exclusivo da classe [Button]. Ele pertence à classe [Control], classe pai da classe [Button].
- EventHandler é um protótipo (um modelo) de método chamado delegado. Voltaremos a esse assunto mais adiante.
- event é uma palavra-chave que restringe as funcionalidades do delegate e do EventHandler: um objeto delegate possui funcionalidades mais abrangentes do que um objeto event.
O delegate EventHandler é definido da seguinte forma:
![]() |
O delegate EventHandler designa um modelo de método:
- cujo primeiro parâmetro é do tipo Object
- cujo segundo parâmetro é do tipo EventArgs
- que não retorna nenhum resultado
Esse é o caso do método de gerenciamento do clique no botão buttonAfficher, que foi gerado pelo Visual Studio:
private void buttonAfficher_Click(object sender, EventArgs e);
Assim, o método buttonAfficher_Click corresponde ao protótipo definido pelo tipo EventHandler. Para criar um objeto do tipo EventHandler, proceda da seguinte forma:
EventHandler evtHandler=new EventHandler(méthode correspondant au prototype défini par le type EventHandler);
Como o método buttonAfficher_Click corresponde ao protótipo definido pelo tipo EventHandler, pode-se escrever:
Uma variável do tipo delegate é, na verdade, uma lista de referências a métodos do tipo delegate. Para adicionar um novo método M à variável evtHandler acima, utilizar-se-á a sintaxe:
A notação += pode ser usada mesmo que evtHandler seja uma lista vazia.
Voltemos à linha de [InitializeComponent], que adiciona um manipulador de evento ao evento Click do objeto buttonAfficher:
this.buttonAfficher.Click += new System.EventHandler(this.buttonAfficher_Click);
Essa instrução adiciona um método do tipo EventHandler à lista de métodos do campo buttonAfficher.Click. Esses métodos serão chamados sempre que o evento Click no componente buttonAfficher for detectado. Geralmente, há apenas um. Ele é chamado de “gerenciador do evento”.
Voltemos à assinatura de EventHandler:
private delegate void EventHandler(object sender, EventArgs e);
O segundo parâmetro do delegate é um objeto do tipo EventArgs ou de uma classe derivada. O tipo EventArgs é muito genérico e, na verdade, não fornece nenhuma informação sobre o evento que ocorreu. Para um clique em um botão, isso é suficiente. Para o movimento do mouse sobre um formulário, teríamos um evento MouseMove da classe [Form] definido por:
O delegate MouseEventHandler é definido como:
![]() |
Trata-se de uma função delegada (delegate) de assinatura void f (object, MouseEventArgs). A classe MouseEventArgs é definida por:
![]() |
A classe MouseEventArgs é mais rica do que a classe EventArgs. É possível, por exemplo, obter as coordenadas X e Y do mouse no momento em que o evento ocorre.
7.1.2.3. Conclusion
A partir dos dois projetos analisados, podemos concluir que, uma vez criada a interface gráfica com o Visual Studio, o trabalho do desenvolvedor consiste principalmente em escrever os manipuladores de eventos que deseja gerenciar para essa interface gráfica. O código é gerado automaticamente pelo Visual Studio. Esse código, que pode ser complexo, pode ser ignorado em uma primeira análise. Posteriormente, seu estudo pode permitir uma melhor compreensão da criação e do gerenciamento de formulários.
7.2. Os componentes básicos
Apresentamos agora diversas aplicações que utilizam os componentes mais comuns, a fim de explorar seus principais métodos e propriedades. Para cada aplicação, apresentamos a interface gráfica e o código relevante, principalmente o dos manipuladores de eventos.
7.2.1. Formulário Form
Começamos apresentando o componente indispensável: o formulário, no qual os componentes são inseridos. Já apresentamos algumas de suas propriedades básicas. Aqui, vamos nos concentrar em alguns eventos importantes de um formulário.
o formulário está sendo carregado | |
o formulário está sendo fechado | |
o formulário está fechado |
O evento Load ocorre antes mesmo de o formulário ser exibido. O evento Closing ocorre quando o formulário está sendo fechado. Ainda é possível interromper esse fechamento por meio de programação.
Criamos um formulário com o nome Form1 sem nenhum componente:
![]() |
- [1]: o formulário
- [2]: os três eventos processados
O código de [Form1.cs] é o seguinte:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
// carregamento inicial do formulário
MessageBox.Show("Evt Load", "Load");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// o formulário está sendo fechado
MessageBox.Show("Evt FormClosing", "FormClosing");
// solicitando confirmação
DialogResult réponse = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (réponse == DialogResult.No)
e.Cancel = true;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
// o formulário será fechado
MessageBox.Show("Evt FormClosed", "FormClosed");
}
}
}
Utilizamos a função MessageBox para sermos notificados sobre os diversos eventos.
linha 10: O evento Load ocorrerá ao iniciar aplicativo, antes mesmo que o formulário seja exibido: | ![]() |
linha 15: O evento FormClosing ocorrerá quando o usuário fechar a janela. | ![]() |
linha 19: Perguntamos então se ele realmente deseja sair o aplicativo: | ![]() |
linha 20: Se ele responder “Não”, definimos a propriedade Cancel do evento CancelEventArgs e que o método recebeu como parâmetro. Se definirmos essa propriedade como False, o fechamento da janela é cancelada; caso contrário, ela prossegue. O evento FormClosed ocorrerá então: | ![]() |
7.2.2. Etiquetas e campos de entrada TextBox
Já nos deparamos com esses dois componentes. Label é um componente de texto e TextBox, um componente de campo de entrada. Sua principal propriedade é Text, que designa tanto o conteúdo do campo de entrada quanto o texto do rótulo. Essa propriedade é de leitura/gravação.
O evento normalmente utilizado para TextBox é TextChanged, que sinaliza que o usuário alterou o campo de entrada. Aqui está um exemplo que utiliza o evento TextChanged para acompanhar as alterações em um campo de entrada:
![]() |
n.º | tipo | nome | função |
1 | TextBox | textBoxSaisie | campo de entrada |
2 | Rótulo | labelControle | exibe o texto de 1 em tempo real AutoSize=False, Text=(nada) |
3 | Botão | buttonEffacer | para apagar os campos 1 e 2 |
4 | Botão | buttonQuitter | para sair do aplicativo |
O código deste aplicativo é o seguinte:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
// o conteúdo de TextBox foi alterado — está sendo copiado para o rótulo labelControle
labelControle.Text = textBoxSaisie.Text;
}
private void buttonEffacer_Click(object sender, System.EventArgs e) {
// o conteúdo do campo de entrada é apagado
textBoxSaisie.Text = "";
}
private void buttonQuitter_Click(object sender, System.EventArgs e) {
// clique no botão Sair — saímos do aplicativo
Application.Exit();
}
private void Form1_Shown(object sender, System.EventArgs e) {
// colocamos o foco no campo de entrada
textBoxSaisie.Focus();
}
}
}
- linha 24: o evento [Form].Shown ocorre quando o formulário é exibido
- linha 26: em seguida, o foco (para entrada de dados) é colocado no componente textBoxSaisie.
- linha 9: o evento [TextBox].TextChanged ocorre sempre que o conteúdo de um componente TextBox é alterado
- linha 11: o conteúdo do componente [TextBox] é copiado para o componente [Label]
- linha 14: gerencia o clique no botão [Effacer]
- linha 16: insere-se a string vazia no componente [TextBox]
- linha 19: processa o clique no botão [Quitter]
- linha 21: para encerrar o aplicativo em execução. Vale lembrar que o objeto Application serve para iniciar o aplicativo no método [Main] de [Form1.cs]:
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
O exemplo a seguir utiliza um TextBox com várias linhas:
![]() |
A lista de controles é a seguinte:
n.º | tipo | nome | função |
1 | TextBox | textBoxLignes | campo de entrada de várias linhas Multiline=true, ScrollBars=Both, AcceptReturn=True, AcceptTab=True |
2 | TextBox | textBoxLigne | campo de entrada de uma linha |
3 | Botão | buttonAjouter | Adiciona o conteúdo de 2 a 1 |
Para que um TextBox se torne multilinha, defina as seguintes propriedades do controle:
para aceitar várias linhas de texto | |
para definir se o controle terá barras de rolagem (Horizontal, Vertical, Both) ou não (None) | |
se for igual a true, a tecla Enter irá avançar para a próxima linha | |
se for igual a true, a tecla Tab gerará uma tabulação no texto |
O aplicativo permite digitar linhas diretamente em [1] ou adicioná-las por meio de [2] e [3].
O código do aplicativo é o seguinte:
using System.Windows.Forms;
using System;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void buttonAjouter_Click(object sender, System.EventArgs e) {
// adiciona o conteúdo de textBoxLigne ao de textBoxLignes
textBoxLignes.Text += textBoxLigne.Text+Environment.NewLine;
textBoxLigne.Text = "";
}
private void Form1_Shown(object sender, EventArgs e) {
// coloca-se o foco no campo de entrada
textBoxLigne.Focus();
}
}
}
- linha 18: quando o formulário é exibido (possivelmente Shown), coloca-se o foco no campo de entrada textBoxLigne
- linha 10: gerencia o clique no botão [Ajouter]
- linha 12: o texto do campo de entrada textBoxLigne é adicionado ao texto do campo de entrada textBoxLignes, seguido de um salto de linha.
- linha 13: o campo de entrada textBoxLigne é apagado
7.2.3. Listas suspensas ComboBox
Criamos o seguinte formulário:
![]() |
n.º | tipo | nome | função |
1 | ComboBox | comboNombres | contém sequências de caracteres DropDownStyle=DropDownList |
Um componente ComboBox é uma lista suspensa acompanhada de um campo de entrada: o usuário pode escolher um item em (2) ou digitar texto em (1). Existem três tipos de ComboBox definidos pela propriedade DropDownStyle:
lista não suspensa com campo de edição | |
lista suspensa com área de edição | |
lista suspensa sem campo de edição |
Por padrão, o tipo de um ComboBox é DropDown.
A classe ComboBox possui um único construtor:
cria uma lista suspensa vazia |
Os elementos do ComboBox estão disponíveis na propriedade Items:
Trata-se de uma propriedade indexada, em que Items[i] designa o elemento i da lista suspensa. Ela é somente para leitura.
Seja C um combo e C.Items sua lista de elementos. Temos as seguintes propriedades:
número de elementos do combo | |
elemento i da lista suspensa | |
adiciona o objeto o como último elemento da lista suspensa | |
adiciona uma matriz de objetos ao final da lista suspensa | |
adiciona o objeto o na posição i da lista suspensa | |
remove o elemento i da lista suspensa | |
remove o objeto o da lista suspensa | |
exclui todos os elementos da lista suspensa | |
retorna a posição i do objeto o na lista suspensa | |
índice do elemento selecionado | |
elemento selecionado | |
texto exibido do elemento selecionado | |
texto exibido do elemento selecionado |
Pode parecer surpreendente que um combo possa conter objetos, embora visualmente exiba cadeias de caracteres. Se um ComboBox contiver um objeto obj, ele exibirá a cadeia obj.ToString(). Vale lembrar que todo objeto possui um método ToString herdado da classe object, que retorna uma sequência de caracteres “representativa” do objeto.
O elemento Item selecionado na lista suspensa C é C.SelectedItem ou C.Items[C.SelectedIndex], onde C.SelectedIndex é o número do elemento selecionado, sendo que esse número começa em zero para o primeiro elemento. O texto selecionado pode ser obtido de várias maneiras: C.SelectedItem.Text, C.Text
Ao selecionar um elemento na lista suspensa, ocorre o evento SelectedIndexChanged, que pode ser utilizado para ser notificado sobre a mudança na seleção da lista suspensa. No aplicativo a seguir, usamos esse evento para exibir o elemento que foi selecionado na lista.
![]() |
O código do aplicativo é o seguinte:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
private int previousSelectedIndex=0;
public Form1() {
InitializeComponent();
// preenchimento da lista suspensa
comboBoxNombres.Items.AddRange(new string[] { "zéro", "un", "deux", "trois", "quatre" });
// seleção do elemento nº 0
comboBoxNombres.SelectedIndex = 0;
}
private void comboBoxNombres_SelectedIndexChanged(object sender, System.EventArgs e) {
int newSelectedIndex = comboBoxNombres.SelectedIndex;
if (newSelectedIndex != previousSelectedIndex) {
// o elemento selecionado mudou — exibimos
MessageBox.Show(string.Format("Elément sélectionné : ({0},{1})", comboBoxNombres.Text, newSelectedIndex), "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information);
// registramos o novo índice
previousSelectedIndex = newSelectedIndex;
}
}
}
}
- linha 5: previousSelectedIndex armazena o último índice selecionado no menu suspenso
- linha 10: preenchimento da lista suspensa com uma matriz de cadeias de caracteres
- linha 12: o primeiro elemento é selecionado
- linha 15: o método executado sempre que o usuário seleciona um elemento da lista suspensa. Ao contrário do que o nome do evento possa sugerir, ele ocorre mesmo que o elemento selecionado seja o mesmo que o anterior.
- linha 16: registra-se o índice do elemento selecionado
- linha 17: se for diferente do anterior
- linha 19: exibe-se o número e o texto do elemento selecionado
- linha 21: registra-se o novo índice
7.2.4. Componente ListBox
Propõe-se a construção da seguinte interface:
![]() |
Os componentes desta janela são os seguintes:
n.º | tipo | nome | função/propriedades |
0 | Form | Form1 | formulário FormBorderStyle=FixedSingle (moldura não redimensionável) |
1 | TextBox | textBoxSaisie | campo de preenchimento |
2 | Botão | buttonAjouter | botão que permite adicionar o conteúdo do campo de entrada [1] à lista [3] |
3 | ListBox | listBox1 | lista 1 SelectionMode=MultiExtended: |
4 | ListBox | listBox2 | lista 2 SelectionMode=MultiSimple: |
5 | Botão | botão1para2 | transfere os elementos selecionados da lista 1 para a lista 2 |
6 | Botão | botão2para1 | faz o contrário |
7 | Botão | buttonEffacer1 | esvazia a lista 1 |
8 | Botão | buttonEffacer2 | esvazia a lista 2 |
Os componentes ListBox possuem um modo de seleção de seus elementos definido pela propriedade SelectionMode:
apenas um elemento pode ser selecionado | |
é possível a seleção múltipla: manter pressionada a tecla SHIFT e clicar em um elemento amplia a seleção do elemento selecionado anteriormente para o elemento atual. | |
É possível selecionar vários itens: um item é selecionado/desmarcado com um clique do mouse ou pressionando a barra de espaço. |
- O usuário digita o texto no campo 1. Ele o adiciona à lista 1 com o botão Ajouter (2). O campo de entrada (1) é então esvaziado e o usuário pode adicionar um novo elemento.
- É possível transferir elementos de uma lista para outra selecionando o elemento a ser transferido em uma das listas e escolhendo o botão de transferência adequado, 5 ou 6. O elemento transferido é adicionado ao final da lista de destino e removido da lista de origem.
- O usuário pode clicar duas vezes em um item da lista 1. Esse item é então transferido para a caixa de entrada para edição e removido da lista 1.
Os botões ficam ativados ou desativados de acordo com as seguintes regras:
- o botão Ajouter só fica aceso se houver um texto não vazio no campo de entrada
- o botão [5] para transferência da lista 1 para a lista 2 fica ativado apenas se houver um elemento selecionado na lista 1
- o botão [6] para transferência da lista 2 para a lista 1 só fica aceso se houver um elemento selecionado na lista 2
- os botões [7] e [8] para apagar as listas 1 e 2 só ficam acesos se a lista a ser apagada contiver itens.
Nas condições acima, todos os botões devem estar desativados ao iniciar o aplicativo. Nesse caso, a propriedade Enabled dos botões deve ser definida como false. Isso pode ser feito na fase de projeto, o que gerará o código correspondente no método InitializeComponent, ou pode ser feito manualmente no construtor, conforme mostrado abaixo:
public Form1() {
InitializeComponent();
// --- inicializações complementares ---
// desativam-se alguns botões
buttonAjouter.Enabled = false;
button1vers2.Enabled = false;
button2vers1.Enabled = false;
buttonEffacer1.Enabled = false;
buttonEffacer2.Enabled = false;
}
O estado do botão Ajouter é controlado pelo conteúdo do campo de entrada. É o evento TextChanged que nos permite acompanhar as alterações nesse conteúdo:
private void textBoxSaisie_TextChanged(object sender, System.EventArgs e) {
// o conteúdo de textBoxSaisie foi alterado
// o botão “Adicionar” só fica ativado se o campo estiver preenchido
buttonAjouter.Enabled = textBoxSaisie.Text.Trim() != "";
}
O estado dos botões de transferência depende do fato de um elemento ter sido selecionado ou não na lista que eles controlam:
private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e) {
// um elemento foi selecionado
// o botão de transferência de 1 para 2 é ativado
button1vers2.Enabled = true;
}
private void listBox2_SelectedIndexChanged(object sender, System.EventArgs e) {
// um elemento foi selecionado
// o botão de transferência de 2 para 1 fica ativado
button2vers1.Enabled = true;
}
O código associado ao clique no botão Ajouter é o seguinte:
private void buttonAjouter_Click(object sender, System.EventArgs e) {
// adicionado um novo elemento à lista 1
listBox1.Items.Add(textBoxSaisie.Text.Trim());
// limpeza do campo de entrada
textBoxSaisie.Text = "";
// A Lista 1 não está vazia
buttonEffacer1.Enabled = true;
// o foco volta para o campo de entrada
textBoxSaisie.Focus();
}
Destaque para o método Focus, que permite colocar o “foco” em um controle do formulário. O código associado ao clique nos botões Effacer:
private void buttonEffacer1_Click(object sender, System.EventArgs e) {
// a lista 1 é apagada
listBox1.Items.Clear();
// botão Apagar
buttonEffacer1.Enabled = false;
}
private void buttonEffacer2_Click(object sender, System.EventArgs e) {
// limpa a lista 2
listBox2.Items.Clear();
// botão Apagar
buttonEffacer2.Enabled = false;
}
O código para transferir os elementos selecionados de uma lista para a outra:
private void button1vers2_Click(object sender, System.EventArgs e) {
// transferência do item selecionado da Lista 1 para a Lista 2
transfert(listBox1, button1vers2, buttonEffacer1, listBox2, button2vers1, buttonEffacer2);
}
private void button2vers1_Click(object sender, System.EventArgs e) {
// transferência do item selecionado da Lista 2 para a Lista 1
transfert(listBox2, button2vers1, buttonEffacer2, listBox1, button1vers2, buttonEffacer1);
}
Os dois métodos acima delegam a transferência dos elementos selecionados de uma lista para outra a um mesmo método privado chamado “transferência”:
// transferência
private void transfert(ListBox l1, Button button1vers2, Button buttonEffacer1, ListBox l2, Button button2vers1, Button buttonEffacer2) {
// transferência para a lista l2 dos elementos selecionados da lista l1
for (int i = l1.SelectedIndices.Count - 1; i >= 0; i--) {
// índice do elemento selecionado
int index = l1.SelectedIndices[i];
// adição à l2
l2.Items.Add(l1.Items[index]);
// exclusão da lista l1
l1.Items.RemoveAt(index);
}
// botões “Apagar”
buttonEffacer2.Enabled = l2.Items.Count != 0;
buttonEffacer1.Enabled = l1.Items.Count != 0;
// botões de transferência
button1vers2.Enabled = false;
}
- linha b: o método “transfert” recebe seis parâmetros:
- uma referência à lista que contém os elementos selecionados, denominada aqui l1. Durante a execução do aplicativo, l1 é listBox1 ou listBox2. Veja exemplos de chamadas nas linhas 3 e 8 dos procedimentos de transferência buttonXversY_Click.
- uma referência ao botão de transferência vinculado à lista l1. Por exemplo, se l1 for listBox2, será button2vers1 (ver chamada na linha 8)
- uma referência no botão de apagamento da lista l1. Por exemplo, se l1 for listBox1, será buttonEffacer1 (ver chamada na linha 3)
- as outras três referências são análogas, mas se referem à lista l2.
- linha d: a coleção [ListBox].SelectedIndices representa os índices dos elementos selecionados no componente [ListBox]. Trata-se de uma coleção:
- [ListBox].SelectedIndices.Count é o número de elementos dessa coleção
- [ListBox].SelectedIndices[i] é o elemento nº i dessa coleção
Percorremos a coleção na ordem inversa: começamos pelo fim da coleção e terminamos pelo início. Explicaremos o motivo.
- linha f: índice de um elemento selecionado da lista l1
- linha h: esse elemento é adicionado à lista l2
- linha j: e removido da lista l1. Como foi removido, ele não está mais selecionado. A coleção l1.SelectedIndices da linha d será recalculada. Ela perderá o elemento que acabou de ser removido. Todos os elementos que vêm depois dele terão seus números alterados de n para n-1.
- Se o ciclo da linha (d) for crescente e tiver acabado de processar o elemento nº 0, ele passará a processar o elemento nº 1. No entanto, o elemento que tinha o nº 1 antes da exclusão do elemento nº 0 passará a ter o nº 0. Ele será, então, ignorado pelo ciclo.
- Se o loop da linha (d) for decrescente e tiver acabado de processar o elemento nº n, ele passará a processar o elemento nº n-1. Após a exclusão do elemento nº n, o elemento nº n-1 não muda de número. Portanto, ele é processado na próxima iteração do loop.
- linhas m-n: o estado dos botões [Effacer] depende da presença ou não de elementos nas listas associadas
- linha p: a lista l2 não tem mais elementos selecionados: desativa-se seu botão de transferência.
7.2.5. Caixas de seleção CheckBox, botões de opção ButtonRadio
Propomos escrever o seguinte aplicativo:
![]() |
Os componentes da janela são os seguintes:
n.º | tipo | nome | função |
1 | GroupBox ver [6] | groupBox1 | um contêiner de componentes. É possível adicionar outros componentes a ele. Texto=Botões de opção |
2 | RadioButton | radioButton1 radioButton2 radioButton3 | 3 botões de opção — radioButton1 possui a propriedade Checked=True e a propriedade Text=1 - radioButton2 possui a propriedade Text=2 - radioButton3 possui a propriedade Text=3 Os botões de opção presentes no mesmo contêiner, neste caso o GroupBox, são mutuamente exclusivos: apenas um deles fica selecionado. |
3 | GroupBox | groupBox2 | |
4 | CheckBox | checkBox1 checkBox2 checkBox3 | 3 caixas de seleção. chechBox1 possui a propriedade Checked=True e a propriedade Text=A - chechBox2 possui a propriedade Text=B - chechBox3 possui a propriedade Text=C |
5 | ListBox | listBoxValeurs | uma lista que exibe os valores dos botões de opção e das caixas de seleção assim que ocorre uma alteração. |
6 | mostra onde encontrar o contêiner GroupBox |
O evento que nos interessa para esses seis controles é o evento CheckChanged, que indica que o estado da caixa de seleção ou do botão de opção mudou. Esse estado é representado, em ambos os casos, pela propriedade booleana Checked, que, quando verdadeira, significa que o controle está marcado. Aqui, utilizaremos apenas um único método para tratar os seis eventos CheckChanged: o método affiche. Para garantir que os seis eventos CheckChanged sejam tratados pelo mesmo método affiche, podemos proceder da seguinte forma:
Selecione o componente radioButton1 e clique com o botão direito do mouse sobre ele para acessar suas propriedades:
![]() |
Na guia événements [1], associamos o método affiche [2] ao evento CheckChanged. Isso significa que se deseja que o clique na opção A1 seja processado por um método chamado affiche. O Visual Studio gera automaticamente o método affiche na janela de código:
private void affiche(object sender, EventArgs e) {
}
O método affiche é um método do tipo EventHandler.
Para os outros cinco componentes, procede-se da mesma forma. Selecionemos, por exemplo, a opção CheckBox1 e seus eventos [3]. Ao lado do evento Click, há uma lista suspensa [4] na qual estão listados os métodos existentes capazes de processar esse evento. Aqui, há apenas o método affiche.. Selecionamos esse método. Repetimos esse processo para todos os demais componentes.
No método InitializeComponent, o código foi gerado. O método affiche foi declarado como manipulador dos seis eventos CheckedChanged da seguinte maneira:
this.radioButton1.CheckedChanged += new System.EventHandler(this.affiche);
this.radioButton2.CheckedChanged += new System.EventHandler(this.affiche);
this.radioButton3.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox1.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox2.CheckedChanged += new System.EventHandler(this.affiche);
this.checkBox3.CheckedChanged += new System.EventHandler(this.affiche);
O método affiche é preenchido da seguinte forma:
private void affiche(object sender, System.EventArgs e) {
// exibe o status do botão de opção ou da caixa de seleção
// isso é uma caixa de seleção?
if (sender is CheckBox) {
CheckBox chk = (CheckBox)sender;
listBoxvaleurs.Items.Add(chk.Name + "=" + chk.Checked);
}
// é um botão de opção?
if (sender is RadioButton) {
RadioButton rdb = (RadioButton)sender;
listBoxvaleurs.Items.Add(rdb.Name + "=" + rdb.Checked);
}
}
A sintaxe
if (sender is CheckBox) {
permite verificar se o objeto sender é do tipo CheckBox. Isso nos permite, em seguida, realizar uma conversão de tipo para o tipo exato de sender. O método affiche grava na lista listBoxValeurs o nome do componente que originou o evento e o valor de sua propriedade Checked. Na execução de [7], observa-se que um clique em um botão de opção provoca dois eventos CheckChanged: um no botão anteriormente marcado, que passa para “desmarcado”, e outro no novo botão, que passa para “marcado”.
7.2.6. Variadores ScrollBar
Existem vários tipos de variadores: o variador horizontal (HscrollBar), o variador vertical (VscrollBar), o incrementador (NumericUpDown). |
Vamos criar a seguinte aplicação:
![]() |
n.º | tipo | nome | função |
1 | hScrollBar | hScrollBar1 | um variador horizontal |
2 | hScrollBar | hScrollBar2 | um variador horizontal que acompanha as variações do variador 1 |
3 | Etiqueta | labelValeurHS1 | exibe o valor do variador horizontal |
4 | NumericUpDown | numericUpDown2 | permite definir o valor do regulador 2 |
Um controle deslizante ScrollBar permite que o usuário escolha um valor dentro de um intervalo de valores inteiros, representado pela “faixa” do controle deslizante sobre a qual um cursor se move. O valor do controle deslizante está disponível na propriedade Value.
- Para um controle deslizante horizontal, a extremidade esquerda representa o valor mínimo do intervalo, a extremidade direita o valor máximo e o cursor o valor atual selecionado. Para um controle deslizante vertical, o mínimo é representado pela extremidade superior e o máximo pela extremidade inferior. Esses valores são representados pelas propriedades Minimum e Maximum e têm valores padrão de 0 e 100.
- Um clique nas extremidades do controle deslizante altera o valor em um incremento (positivo ou negativo), dependendo da extremidade clicada, denominada SmallChange, cujo valor padrão é 1.
- Um clique em qualquer lado do cursor altera o valor em um incremento (positivo ou negativo), dependendo da extremidade clicada, denominada LargeChange, cujo valor padrão é 10.
- Ao clicar na extremidade superior de um controle deslizante vertical, seu valor diminui. Isso pode surpreender o usuário comum, que normalmente espera ver o valor “aumentar”. Esse problema é resolvido atribuindo-se um valor negativo às propriedades SmallChange e LargeChange
- Essas cinco propriedades (Value, Minimum, Maximum, SmallChange, LargeChange) estão acessíveis para leitura e gravação.
- O principal evento do inversor é aquele que sinaliza uma alteração de valor: o evento Scroll.
Um componente NumericUpDown está próximo ao variador: ele também possui as propriedades Minimum, Maximum e Value, com valores padrão 0, 100, 0. Mas, neste caso, a propriedade Value é exibida em um campo de entrada que faz parte integrante do controle. O usuário pode alterar esse valor por conta própria, a menos que a propriedade ReadOnly do controle tenha sido definida como verdadeira. O valor do incremento é definido pela propriedade Increment, cujo valor padrão é 1. O principal evento do componente NumericUpDown é aquele que sinaliza uma alteração de valor: o evento ValueChanged
O código do aplicativo é o seguinte:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
// definimos as características do variador 1
hScrollBar1.Value = 7;
hScrollBar1.Minimum = 1;
hScrollBar1.Maximum = 130;
hScrollBar1.LargeChange = 11;
hScrollBar1.SmallChange = 1;
// atribuímos ao variador 2 as mesmas características do variador 1
hScrollBar2.Value = hScrollBar1.Value;
hScrollBar2.Minimum = hScrollBar1.Minimum;
hScrollBar2.Maximum = hScrollBar1.Maximum;
hScrollBar2.LargeChange = hScrollBar1.LargeChange;
hScrollBar2.SmallChange = hScrollBar1.SmallChange;
// o mesmo vale para o incrementador
numericUpDown2.Value = hScrollBar1.Value;
numericUpDown2.Minimum = hScrollBar1.Minimum;
numericUpDown2.Maximum = hScrollBar1.Maximum;
numericUpDown2.Increment = hScrollBar1.SmallChange;
// atribui-se ao Label o valor do variador 1
labelValeurHS1.Text = hScrollBar1.Value.ToString();
}
private void hScrollBar1_Scroll(object sender, ScrollEventArgs e) {
// alteração do valor do variador 1
// seu valor é repassado para o variador 2 e para o Label
hScrollBar2.Value = hScrollBar1.Value;
labelValeurHS1.Text = hScrollBar1.Value.ToString();
}
private void numericUpDown2_ValueChanged(object sender, System.EventArgs e) {
// o incrementador mudou de valor
// define-se o valor do variador 2
hScrollBar2.Value = (int)numericUpDown2.Value;
}
}
}
7.3. Eventos do mouse
Ao desenhar em um contêiner, é importante saber a posição do mouse para, por exemplo, exibir um ponto ao clicar. Os movimentos do mouse geram eventos no contêiner em que ele se move.
![]() |
- [1]: eventos que ocorrem durante o deslocamento do mouse sobre o formulário ou um controle
- [2]: eventos que ocorrem durante um arrastar e soltar (Drag'nDrop)
o mouse acaba de entrar na área do controle | |
o mouse acaba de sair da área do controle | |
o mouse está se movendo na área de controle | |
Pressão no botão esquerdo do mouse | |
Solte o botão esquerdo do mouse | |
o usuário solta um objeto no controle | |
o usuário entra na área do controle arrastando um objeto | |
o usuário sai da área do controle arrastando um objeto | |
o usuário passa por cima da área do controle ao arrastar um objeto |
Aqui está um aplicativo que ajuda a entender melhor em quais momentos ocorrem os diferentes eventos do mouse:
![]() |
n.º | tipo | nome | função |
1 | Etiqueta | lblPositionSouris | para exibir a posição do mouse no formulário 1, na lista 2 ou no botão 3 |
2 | ListBox | listBoxEvts | para exibir eventos do mouse diferentes de MouseMove |
3 | Botão | buttonEffacer | para apagar o conteúdo de 2 |
Para acompanhar os movimentos do mouse nos três controles, escrevemos apenas um único manipulador, o manipulador affiche:
![]() |
O código do procedimento affiche é o seguinte:
private void affiche(object sender, MouseEventArgs e) {
// movimento do mouse — exibe-se as coordenadas (X, Y) do mouse
labelPositionSouris.Text = "(" + e.X + "," + e.Y + ")";
}
Sempre que o mouse entra no domínio de um controle, seu sistema de coordenadas muda. Sua origem (0,0) é o canto superior esquerdo do controle sobre o qual ele se encontra. Assim, durante a execução, ao passar o mouse do formulário para o botão, percebe-se claramente a mudança nas coordenadas. Para visualizar melhor essas mudanças de área do mouse, pode-se utilizar a propriedade Cursor [1] dos controles:
![]() |
Essa propriedade permite definir a forma do cursor do mouse quando ele entra na área do controle. Assim, em nosso exemplo, definimos o cursor como “Default” para o próprio formulário [2], “Hand” para a lista 2 [3] e “Cross” para o botão 3 [4].
Além disso, para detectar as entradas e saídas do mouse na lista 2, processamos os eventos MouseEnter e MouseLeave dessa mesma lista:
private void listBoxEvts_MouseEnter(object sender, System.EventArgs e) {
// se sinaliza o evento
listBoxEvts.Items.Insert(0, string.Format("MouseEnter à {0:hh:mm:ss}",DateTime.Now));
}
private void listBoxEvts_MouseLeave(object sender, EventArgs e) {
// se sinaliza o evento
listBoxEvts.Items.Insert(0, string.Format("MouseLeave à {0:hh:mm:ss}", DateTime.Now));
}
Para processar os cliques no formulário, tratamos os eventos MouseDown e MouseUp:
private void listBoxEvts_MouseDown(object sender, MouseEventArgs e) {
// notifica-se o evento
listBoxEvts.Items.Insert(0, string.Format("MouseDown à {0:hh:mm:ss}", DateTime.Now));
}
private void listBoxEvts_MouseUp(object sender, MouseEventArgs e) {
// notifica o evento
listBoxEvts.Items.Insert(0, string.Format("MouseUp à {0:hh:mm:ss}", DateTime.Now));
}
- linhas 3 e 8: as mensagens são colocadas na primeira posição no ListBox para que os eventos mais recentes sejam os primeiros da lista.
![]() |
Por fim, o código do manipulador de clique no botão Effacer:
private void buttonEffacer_Click(object sender, EventArgs e) {
listBoxEvts.Items.Clear();
}
7.4. Criar uma janela com menu
Vamos ver agora como criar uma janela com menu. Vamos criar a seguinte janela:
![]() |
Para criar um menu, selecione o componente “MenuStrip” na barra “Menus & Tollbars”:
![]() |
- [1]: seleção do componente [MenuStrip]
- [2]: surge então um menu que é inserido no formulário com campos em branco intitulados “Digite aqui”. Basta inserir as diferentes opções do menu nesses campos.
- [3]: o texto “Opções A” foi digitado. Passamos para o texto [4].
- [5]: os nomes das opções A foram inseridos. Passamos para o nome [6]
![]() |
- [6]: as primeiras opções B
- [7]: abaixo de B1, insere-se um separador. Ele está disponível em uma lista suspensa associada ao texto “Type Here”
- [8]: para criar um submenu, use a seta [8] e digite o submenu em [9]
Resta nomear os diferentes componentes do formulário:
![]() |
n.º | tipo | nome(s) | função |
1 | Etiqueta | labelStatut | para exibir o texto da opção do menu selecionada |
2 | toolStripMenuItem | toolStripMenuItemOptionsA toolStripMenuItemA1 toolStripMenuItemA2 toolStripMenuItemA3 | opções de menu na opção principal “Opções A” |
3 | toolStripMenuItem | toolStripMenuItemOptionsB toolStripMenuItemB1 toolStripMenuItemB2 toolStripMenuItemB3 | opções de menu na opção principal “Opções B” |
4 | toolStripMenuItem | toolStripMenuItemB31 toolStripMenuItemB32 | opções de menu sob a opção principal “B3” |
As opções de menu são controles, assim como os demais componentes visuais, e possuem propriedades e eventos. Por exemplo, as propriedades da opção de menu A1 são as seguintes:
![]() |
Duas propriedades são utilizadas em nosso exemplo:
o nome do controle de menu | |
o texto da opção do menu |
Na estrutura do menu, selecione a opção A1 e clique com o botão direito do mouse para acessar as propriedades do controle:
![]() |
Na guia événements [1], associamos o método affiche [2] ao evento Click. Isso significa que se deseja que o clique na opção A1 seja processado por um método chamado affiche. O Visual Studio gera automaticamente o método affiche na janela de código:
private void affiche(object sender, EventArgs e) {
}
Nesse método, nos limitaremos a exibir no rótulo labelStatut a propriedade Text da opção de menu que foi clicada:
private void affiche(object sender, EventArgs e) {
// exibe no TextBox o nome do submenu selecionado
labelStatut.Text = ((ToolStripMenuItem)sender).Text;
}
A origem do evento sender é do tipo object. As opções de menu são do tipo ToolStripMenuItem; portanto, é necessário realizar uma conversão de tipo de object para ToolStripMenuItem.
Para todas as opções de menu, definimos o gerenciador de cliques como o método affiche [3,4].
Vamos executar o aplicativo e selecionar um item do menu:
![]() | ![]() |
7.5. Componentes não visuais
Vamos nos concentrar agora em alguns componentes não visuais: eles são utilizados durante o projeto, mas não são visíveis durante a execução.
7.5.1. Caixas de diálogo , OpenFileDialog e SaveFileDialog
Vamos criar o seguinte aplicativo:
![]() |
Os controles são os seguintes:
Nº | tipo | nome | função |
1 | TextBox | TextBoxLignes | texto digitado pelo usuário ou carregado de um arquivo MultiLine=True, ScrollBars=Both, AccepReturn=True, AcceptTab=True |
2 | Botão | buttonSauvegarder | permite salvar o texto de [1] em um arquivo de texto |
3 | Botão | buttonCharger | permite carregar o conteúdo de um arquivo de texto no [1] |
4 | Botão | buttonEffacer | apaga o conteúdo de [1] |
5 | SaveFileDialog | saveFileDialog1 | componente que permite escolher o nome e o local do arquivo de backup de [1]. Esse componente é selecionado na barra de ferramentas [7] e simplesmente arrastado para o formulário. Ele é então registrado, mas não ocupa espaço no formulário. Trata-se de um componente não visual. |
6 | OpenFileDialog | openFileDialog1 | Componente que permite selecionar o arquivo a ser carregado no [1]. |
O código associado ao botão Effacer é simples:
private void buttonEffacer_Click(object sender, EventArgs e) {
// insere a string vazia no TexBox
textBoxLignes.Text = "";
}
Utilizaremos as seguintes propriedades e métodos da classe SaveFileDialog:
Campo | Tipo | Função |
Propriété | os tipos de arquivo disponíveis na lista suspensa de tipos de arquivo da caixa de diálogo | |
Propriété | o número do tipo de arquivo sugerido por padrão na lista acima. Começa em 0. | |
Propriété | a pasta indicada inicialmente para salvar o arquivo | |
Propriété | o nome do arquivo de backup indicado pelo usuário | |
Méthode | método que exibe a caixa de diálogo de salvamento. Retorna um resultado do tipo DialogResult. |
O método ShowDialog exibe uma caixa de diálogo semelhante à seguinte:
![]() |
lista suspensa criada a partir da propriedade Filter. O tipo de arquivo proposto por padrão é definido por FilterIndex | |
pasta atual, definida por InitialDirectory caso essa propriedade tenha sido preenchida | |
nome do arquivo escolhido ou digitado diretamente pelo usuário. Estará disponível na propriedade FileName | |
botões Salvar/Cancelar. Se o botão Enregistrer for utilizado, a função ShowDialog retorna o resultado DialogResult.OK |
O procedimento de salvamento pode ser escrito da seguinte forma:
private void buttonSauvegarder_Click(object sender, System.EventArgs e) {
// salva-se o campo de entrada em um arquivo de texto
// configura-se a caixa de diálogo savefileDialog1
saveFileDialog1.InitialDirectory = Application.ExecutablePath;
saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
saveFileDialog1.FilterIndex = 0;
// exibe-se a caixa de diálogo e recupera-se seu resultado
if (saveFileDialog1.ShowDialog() == DialogResult.OK) {
// recupera-se o nome do arquivo
string nomFichier = saveFileDialog1.FileName;
StreamWriter fichier = null;
try {
// abre-se o arquivo para gravação
fichier = new StreamWriter(nomFichier);
// escreve-se o texto nele
fichier.Write(textBoxLignes.Text);
} catch (Exception ex) {
// problema
MessageBox.Show("Problème à l'écriture du fichier (" +
ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
} finally {
// fecha-se o arquivo
if (fichier != null) {
fichier.Dispose();
}
}
}
}
- linha 4: define-se a pasta inicial (InitialDirectory) como a pasta (Application.ExecutablePath) que contém o executável do aplicativo.
- linha 5: definem-se os tipos de arquivos a serem apresentados. Observe-se a sintaxe dos filtros: filtre1|filtre2|..|filtren com filtro i = Texto|modelo de arquivo. Aqui, o usuário poderá escolher entre os arquivos *.txt e *.*.
- linha 6: define-se o tipo de arquivo a ser apresentado primeiro ao usuário. Aqui, o índice 0 refere-se aos arquivos *.txt.
- linha 8: a caixa de diálogo é exibida e seu resultado é recuperado. Enquanto a caixa de diálogo estiver exibida, o usuário não terá mais acesso ao formulário principal (caixa de diálogo chamada modal). O usuário define o nome do arquivo a ser salvo e sai da caixa de diálogo clicando no botão Enregistrer, no botão Annuler, ou fechando a caixa de diálogo. O resultado do método ShowDialog é DialogResult.OK apenas se o usuário tiver usado o botão Enregistrer para sair da caixa de diálogo.
- Feito isso, o nome do arquivo a ser criado está agora na propriedade FileName do objeto saveFileDialog1. Chega-se então à criação clássica de um arquivo de texto. Nele, escreve-se o conteúdo de TextBox: textBoxLignes.Text, ao mesmo tempo em que se gerencia as exceções que possam ocorrer.
A classe OpenFileDialog é muito semelhante à classe SaveFileDialog. Utilizaremos os mesmos métodos e propriedades de antes. O método ShowDialog exibe uma caixa de diálogo semelhante à seguinte:
![]() |
lista suspensa criada a partir da propriedade Filter. O tipo de arquivo proposto por padrão é definido por FilterIndex | |
pasta atual, definida por InitialDirectory caso essa propriedade tenha sido preenchida | |
nome do arquivo escolhido ou digitado diretamente pelo usuário. Estará disponível na propriedade FileName | |
botões Abrir/Cancelar. Se for utilizado o botão Ouvrir, a função ShowDialog retorna o resultado DialogResult.OK |
O procedimento para carregar o arquivo de texto pode ser escrito da seguinte forma:
private void buttonCharger_Click(object sender, EventArgs e) {
// carrega-se um arquivo de texto na caixa de entrada
// configura-se a caixa de diálogo openfileDialog1
openFileDialog1.InitialDirectory = Application.ExecutablePath;
openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
openFileDialog1.FilterIndex = 0;
// exibe-se a caixa de diálogo e recupera-se seu resultado
if (openFileDialog1.ShowDialog() == DialogResult.OK) {
// recupera-se o nome do arquivo
string nomFichier = openFileDialog1.FileName;
StreamReader fichier = null;
try {
// abre-se o arquivo para leitura
fichier = new StreamReader(nomFichier);
// lê-se todo o arquivo e ele é colocado no TextBox
textBoxLignes.Text = fichier.ReadToEnd();
} catch (Exception ex) {
// problema
MessageBox.Show("Problème à la lecture du fichier (" +
ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
} finally {
// fecha-se o arquivo
if (fichier != null) {
fichier.Dispose();
}
}//finalmente
}//se
}
- linha 4: define-se a pasta inicial (InitialDirectory) como a pasta (Application.ExecutablePath) que contém o executável do aplicativo.
- linha 5: definem-se os tipos de arquivos a serem apresentados. Observe-se a sintaxe dos filtros: filtre1|filtre2|..|filtren com filtro i = Texto|modelo de arquivo. Aqui, o usuário poderá escolher entre os arquivos *.txt e *.*.
- linha 6: define-se o tipo de arquivo a ser apresentado primeiro ao usuário. Aqui, o índice 0 refere-se aos arquivos *.txt.
- linha 8: a caixa de diálogo é exibida e seu resultado é recuperado. Enquanto a caixa de diálogo estiver exibida, o usuário não terá mais acesso ao formulário principal (caixa de diálogo chamada modal). O usuário define o nome do arquivo a ser salvo e sai da caixa de diálogo clicando no botão Ouvrir, no botão Annuler, ou fechando a caixa de diálogo. O resultado do método ShowDialog é DialogResult.OK apenas se o usuário tiver usado o botão Enregistrer para sair da caixa de diálogo.
- Feito isso, o nome do arquivo a ser criado está agora na propriedade FileName do objeto openFileDialog1. Passa-se então para a leitura clássica de um arquivo de texto. Observe, na linha 16, o método que permite ler um arquivo na íntegra.
7.5.2. Caixas de diálogo FontColor e ColorDialog
Continuamos o exemplo anterior, adicionando dois novos botões e dois novos controles não visuais:
![]() |
6
7
N.º | tipo | nome | função |
1 | Botão | buttonCouleur | para definir a cor dos caracteres do TextBox |
2 | Botão | buttonPolice | para definir a fonte do TextBox |
3 | ColorDialog | colorDialog1 | o componente que permite a seleção de uma cor — extraído da caixa de ferramentas [5]. |
4 | FontDialog | colorDialog1 | o componente que permite a seleção de um tipo de letra — incluído na caixa de ferramentas [5]. |
As classes FontDialog e ColorDialog possuem um método ShowDialog análogo ao método ShowDialog das classes OpenFileDialog e SaveFileDialog.
![]() |
O método ShowDialog da classe ColorDialog permite selecionar uma cor [1]. O método da classe FontDialog permite selecionar uma fonte [2]:
- [1]: se o usuário fechar a caixa de diálogo com o botão OK, o resultado do método ShowDialog é DialogResult.OK e a cor selecionada está na propriedade Color do objeto ColorDialog utilizado.
- [2]: se o usuário fechar a caixa de diálogo usando o botão OK, o resultado do método ShowDialog é DialogResult.OK e a fonte selecionada está na propriedade Font do objeto FontDialog utilizado.
Agora temos os elementos necessários para processar os cliques nos botões Couleur e Police:
private void buttonCouleur_Click(object sender, EventArgs e) {// escolha de uma cor de texto
if (colorDialog1.ShowDialog() == DialogResult.OK) {
// alterando a propriedade Forecolor do TextBox
textBoxLignes.ForeColor = colorDialog1.Color;
}//se
}
private void buttonPolice_Click(object sender, EventArgs e) {
// escolha de uma fonte
if (fontDialog1.ShowDialog() == DialogResult.OK) {
// alteração da propriedade Font do TextBox
textBoxLignes.Font = fontDialog1.Font;
}
- linha [4]: a propriedade [ForeColor] de um componente TextBox define a cor do tipo [Color] dos caracteres do TextBox. Nesse caso, essa cor é a escolhida pelo usuário na caixa de diálogo do tipo [ColorDialog].
- linha [12]: a propriedade [Font] de um componente TextBox designa a fonte do tipo [Font] dos caracteres do TextBox. Nesse caso, essa fonte é a escolhida pelo usuário na caixa de diálogo do tipo [FontDialog].
7.5.3. Temporizador
Propomos aqui escrever o seguinte aplicativo:
![]() |
n.º | Tipo | Nome | Função |
1 | Marca | labelChrono | exibe um cronômetro |
2 | Botão | buttonArretMarche | botão de parar/iniciar o cronômetro |
3 | Temporizador | timer1 | componente que emite um evento a cada segundo |
Em [4], vemos o cronômetro em funcionamento; em [5], o cronômetro está parado.
Para alterar a cada segundo o conteúdo do Label LabelChrono, precisamos de um componente que gere um evento a cada segundo, evento esse que poderemos interceptar para atualizar a exibição do cronômetro. Esse componente é o Timer [1], disponível na caixa de ferramentas Components [2]:
![]() |
As propriedades do componente Timer utilizadas aqui serão as seguintes:
número de milissegundos após o qual um evento Tick é emitido. | |
o evento gerado ao final de Interval milissegundos | |
torna o temporizador ativo (true) ou inativo (false) |
No nosso exemplo, o temporizador se chama timer1 e timer1.Interval está definido para 1000 ms (1 s). O evento Tick ocorrerá, portanto, a cada segundo. O clique no botão Parar/Iniciar é processado pela seguinte rotina buttonArretMarche_Click:
using System;
using System.Windows.Forms;
namespace Chap5 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
// variável de instância
private DateTime début = DateTime.Now;
...
private void buttonArretMarche_Click(object sender, EventArgs e) {
// parar ou continuar?
if (buttonArretMarche.Text == "Marche") {
// registra-se a hora de início
début = DateTime.Now;
// exibimos
labelChrono.Text = "00:00:00";
// inicia-se o cronômetro
timer1.Enabled = true;
// alterar o texto do botão
buttonArretMarche.Text = "Arrêt";
// fim
return;
}//
if (buttonArretMarche.Text == "Arrêt") {
// parada do cronômetro
timer1.Enabled = false;
// alterar o texto do botão
buttonArretMarche.Text = "Marche";
// fim
return;
}
}
}
}
- linha 13: o procedimento que processa o clique no botão Parar/Iniciar.
- linha 15: o texto do botão Parar/Iniciar é “Parar” ou “Iniciar”. Portanto, é necessário verificar esse texto para saber o que fazer.
- linha 17: no caso de “Ligar”, registra-se a hora de início em uma variável début, que é uma variável global (linha 11) do objeto formulário
- linha 19: inicializa o conteúdo do rótulo LabelChrono
- linha 21: o temporizador é iniciado (Enabled=true)
- linha 23: o texto do botão muda para “Parar”.
- linha 27: no caso de “Parar”
- linha 29: o temporizador é parado (Enabled=false)
- linha 31: o texto do botão passa para “Ligar”.
Resta-nos tratar o evento Tick no objeto timer1, evento que ocorre a cada segundo:
private void timer1_Tick(object sender, EventArgs e) {
// passou um segundo
DateTime maintenant = DateTime.Now;
TimeSpan durée = maintenant - début;
// atualização do cronômetro
labelChrono.Text = durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2");
}
- linha 3: registramos a hora atual
- linha 4: calcula-se o tempo decorrido desde o momento em que o cronômetro foi iniciado. Obtém-se um objeto do tipo TimeSpan, que representa um intervalo de tempo.
- linha 6: esta deve ser exibida no cronômetro na forma hh:mm:ss. Para isso, utilizamos as propriedades Hours, Minutes, Seconds do objeto TimeSPan, que representam, respectivamente, as horas, minutos e segundos da duração que exibimos no formato ToString("d2") para obter uma exibição com 2 dígitos.
7.6. Aplicativo de exemplo — versão 6
Retomamos a aplicação de exemplo IMPOTS. A versão mais recente foi analisada no parágrafo 6.4. Tratava-se da seguinte aplicação de três camadas:
![]() |
- as camadas [metier] e [dao] estavam encapsuladas em DLL
- a camada [ui] era uma camada [console]
- a instanciação das camadas e sua integração na aplicação eram garantidas pelo Spring.
Nesta nova versão, a camada [ui] será implementada pela seguinte interface gráfica:
![]() |
7.6.1. A solução do Visual Studio
A solução do Visual Studio é composta pelos seguintes elementos:
![]() |
- [1]: o projeto é composto pelos seguintes elementos:
- [Program.cs]: a classe que inicia o aplicativo
- [Form1.cs]: a classe de um primeiro formulário
- [Form2]: a classe de um segundo formulário
- [lib], detalhado em [2]: nele foram incluídos todos os DLL necessários para o projeto:
- [ImpotsV5-dao.dll]: o DLL da camada [dao] gerado no parágrafo 6.4.3;
- [ImpotsV5-metier.dll]: o DLL da camada [dao] gerada no parágrafo 6.4.4;
- [Spring.Core.dll], [Common.Logging.dll], [antlr.runtime.dll]: os DLL do Spring já utilizados na versão anterior (ver parágrafo 6.4.6).
- [references], detalhado em [3]: as referências do projeto. Foi adicionada uma referência para cada um dos arquivos DLL da pasta [lib]
- [App.config]: o arquivo de configuração do projeto. É idêntico ao da versão anterior, descrito no parágrafo 6.4.6;
- [DataImpot.txt]: o arquivo das faixas de imposto configurado para ser copiado automaticamente para a pasta de execução do projeto [4]
O formulário [Form1] é o formulário para inserção dos parâmetros do cálculo do imposto [A], já apresentado anteriormente. O formulário [Form2] [B] serve para exibir uma mensagem de erro:
![]() |
7.6.2. A classe [Program.cs]
A classe [Program.cs] inicia o aplicativo. Seu código é o seguinte:
using System;
using System.Windows.Forms;
using Spring.Context;
using Spring.Context.Support;
using Metier;
using System.Text;
namespace Chap5 {
static class Program {
/// <summary>
/// O ponto de entrada principal do aplicativo.
/// </summary>
[STAThread]
static void Main() {
// código gerado pelo Visual Studio
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// --------------- Código do desenvolvedor
// instâncias das camadas [metier] e [dao]
IApplicationContext ctx = null;
Exception ex = null;
IImpotMetier metier = null;
try {
// contexto Spring
ctx = ContextRegistry.GetContext();
// é solicitada uma referência na camada [metier]
metier = (IImpotMetier)ctx.GetObject("metier");
} catch (Exception e1) {
// armazenamento de exceção
ex = e1;
}
// formulário a ser exibido
Form form = null;
// houve alguma exceção?
if (ex != null) {
// sim — cria-se a mensagem de erro a ser exibida
StringBuilder msgErreur = new StringBuilder(String.Format("Chaîne des exceptions : {0}{1}", "".PadLeft(40, '-'), Environment.NewLine));
Exception e = ex;
while (e != null) {
msgErreur.Append(String.Format("{0}: {1}{2}", e.GetType().FullName, e.Message, Environment.NewLine));
msgErreur.Append(String.Format("{0}{1}", "".PadLeft(40, '-'), Environment.NewLine));
e = e.InnerException;
}
// criação da janela de erro para a qual é passada a mensagem de erro a ser exibida
Form2 form2 = new Form2();
form2.MsgErreur = msgErreur.ToString();
// essa será a janela a ser exibida
form = form2;
} else {
// tudo correu bem
// criação da interface gráfica [Form1], para a qual é passada a referência na camada [metier]
Form1 form1 = new Form1();
form1.Metier = metier;
// essa será a janela a ser exibida
form = form1;
}
// exibição da janela
Application.Run(form);
}
}
}
O código gerado pelo Visual Studio foi complementado a partir da linha 19. O aplicativo utiliza o arquivo [App.config] e a seguir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object name="dao" type="Dao.FileImpot, ImpotsV5-dao">
<constructor-arg index="0" value="DataImpot.txt"/>
</object>
<object name="metier" type="Metier.ImpotMetier, ImpotsV5-metier">
<constructor-arg index="0" ref="dao"/>
</object>
</objects>
</spring>
</configuration>
- linhas 24-32: utilização do arquivo [App.config] anterior para instanciar as camadas [metier] e [dao]
- linha 26: processamento do arquivo [App.config]
- linha 28: recuperação de uma referência na camada [metier]
- linha 31: registro de uma eventual exceção
- linha 34: a referência form indicará o formulário a ser exibido (form1 ou form2)
- linhas 36-50: se houver uma exceção, prepara-se a exibição de um formulário do tipo [Form2]
- linhas 38-44: constrói-se a mensagem de erro a ser exibida. Ela é formada pela concatenação das mensagens de erro das diferentes exceções presentes na cadeia de exceções.
- linha 46: é criado um formulário do tipo [Form2].
- linha 47: como veremos mais adiante, esse formulário possui uma propriedade pública MsgErreur, que é a mensagem de erro a ser exibida:
public string MsgErreur { private get; set; }
Preenche-se essa propriedade.
- linha 49: a referência form, que designa a janela a ser exibida, é inicializada. Observe-se o polimorfismo em ação. form2 não é do tipo [Form], mas sim do tipo [Form2], um tipo derivado de [Form].
- linhas 50-57: não houve exceção. Preparamos a exibição de um formulário do tipo [Form1].
- linha 53: é criado um formulário do tipo [Form1].
- linha 54: como veremos mais adiante, esse formulário possui uma propriedade pública Metier, que é uma referência à camada [metier]:
public IImpotMetier Metier { private get; set; }
Preenche-se essa propriedade.
- linha 56: a referência form, que designa a janela a ser exibida, é inicializada. Observe-se novamente o polimorfismo em ação. form1 não é do tipo [Form], mas do tipo [Form1], um tipo derivado de [Form].
- linha 59: a janela referenciada por form é exibida.
7.6.3. O formulário [Form1]
No modo [conception], o formulário [Form1] é o seguinte:
![]() |
Os controles são os seguintes
n.º | tipo | nome | função |
0 | GroupBox | groupBox1 | Text=Você é casado(a)? |
1 | RadioButton | radioButtonOui | marque se for casado(a) |
2 | RadioButton | radioButtonNon | marcado se solteiro Checked=True |
3 | NumericUpDown | numericUpDownEnfants | número de filhos do contribuinte Mínimo=0, Máximo=20, Incremento=1 |
4 | TextBox | textSalaire | salário anual do contribuinte em euros |
5 | Rótulo | labelImpot | valor do imposto a pagar BorderStyle=Fixed3D |
6 | Botão | buttonCalculer | inicia o cálculo do imposto |
7 | Botão | buttonEffacer | recupera o formulário no estado em que se encontrava no momento do carregamento |
8 | Botão | buttonQuitter | para sair do aplicativo |
Regras de funcionamento do formulário
- o botão Calculer permanece desativado enquanto não houver nada no campo do salário
- se, ao iniciar o cálculo, for constatado que o salário está incorreto, o erro é sinalizado [9]
O código da classe é o seguinte:
using System.Windows.Forms;
using Metier;
using System;
namespace Chap5 {
public partial class Form1 : Form {
// camada [métier]
public IImpotMetier Metier { private get; set; }
public Form1() {
InitializeComponent();
}
private void buttonCalculer_Click(object sender, System.EventArgs e) {
// o salário está correto?
int salaire;
bool ok=int.TryParse(textSalaire.Text.Trim(), out salaire);
if (! ok || salaire < 0) {
// mensagem de erro
MessageBox.Show("Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error);
// retorno ao campo com erro
textSalaire.Focus();
// seleção do texto do campo de preenchimento
textSalaire.SelectAll();
// retorno à interface de preenchimento
return;
}
// o salário está correto — é possível calcular o imposto
labelImpot.Text = Metier.CalculerImpot(radioButtonOui.Checked, (int)numericUpDownEnfants.Value, salaire).ToString();
}
private void buttonQuitter_Click(object sender, System.EventArgs e) {
Environment.Exit(0);
}
private void buttonEffacer_Click(object sender, System.EventArgs e) {
// limpar formulário
labelImpot.Text = "";
numericUpDownEnfants.Value = 0;
textSalaire.Text = "";
radioButtonNon.Checked = true;
}
private void textSalaire_TextChanged(object sender, EventArgs e) {
// estado do botão [Calculer]
buttonCalculer.Enabled=textSalaire.Text.Trim()!="";
}
}
}
Comentamos apenas as partes importantes:
- linha [8]: a propriedade pública Metier, que permite que a classe de execução [Program.cs] insira na [Form1] uma referência à camada [metier].
- linha [14]: o procedimento de cálculo do imposto
- linhas 15-27: verificação da validade do salário (um número inteiro >=0).
- linha 29: cálculo do imposto utilizando o método [CalculerImpot] da camada [metier]. Vale destacar a simplicidade dessa operação, obtida graças ao encapsulamento da camada [metier] na camada DLL.
7.6.4. O formulário [Form2]
No modo [conception], o formulário [Form2] é o seguinte:
![]() |
As verificações são as seguintes
n.º | tipo | nome | função |
1 | TextBox | textBoxErreur | Multiline=True, Scrollbars=Both |
O código da classe é o seguinte:
using System.Windows.Forms;
namespace Chap5 {
public partial class Form2 : Form {
// mensagem de erro
public string MsgErreur { private get; set; }
public Form2() {
InitializeComponent();
}
private void Form2_Load(object sender, System.EventArgs e) {
// exibe a mensagem de erro
textBoxErreur.Text = MsgErreur;
// desmarca todo o texto
textBoxErreur.Select(0, 0);
}
}
}
- linha 6: a propriedade pública MsgErreur, que permite que a classe de lançamento [Program.cs] insira na [Form2] a mensagem de erro a ser exibida. Essa mensagem é exibida durante o processamento do evento Load, linhas 12 a 16.
- linha 14: a mensagem de erro é inserida no TextBox
- linha 16: remove-se a seleção realizada na operação anterior. [TextBox].Select(início,comprimento) seleciona (destaca) longueur caracteres a partir do caractere nº début. [TextBox].Select(0,0) desmarca todo o texto.
7.6.5. Conclusão
Voltemos à arquitetura de três camadas utilizada:
![]() |
Essa arquitetura nos permitiu substituir a implementação de console da camada [ui] existente por uma implementação gráfica, sem alterar nada nas camadas [metier] e [dao]. Pudemos nos concentrar na camada [ui] sem nos preocuparmos com possíveis impactos nas outras camadas. Esse é o principal benefício das arquiteturas de três camadas. Veremos outro exemplo mais adiante, quando a camada [dao], que atualmente utiliza os dados de um arquivo de texto, for substituída por uma camada [dao] que utiliza os dados de um banco de dados. Veremos que isso ocorrerá sem impacto nas camadas [ui] e [metier].


































































