Skip to content

5. Interfaces gráficas com VB.NET e VS.NET

Aqui, pretendemos demonstrar como criar interfaces gráficas de utilizador com VB.NET. Primeiro, iremos examinar as classes principais da plataforma .NET que nos permitem criar uma interface gráfica de utilizador. Inicialmente, não utilizaremos quaisquer ferramentas de geração automática. Em seguida, utilizaremos o Visual Studio.NET (VS.NET), uma ferramenta de desenvolvimento da Microsoft que facilita o desenvolvimento de aplicações utilizando linguagens .NET, em particular a criação de interfaces gráficas de utilizador. A versão do VS.NET utilizada é a versão em inglês.

5.1. Noções básicas sobre interfaces gráficas de utilizador

5.1.1. Uma janela simples

Considere o seguinte código:


' options
Option Strict On
Option Explicit On 
 
' namespaces
Imports System
Imports System.Drawing
Imports System.Windows.Forms
 
' the form class
Public Class Form1
    Inherits Form
 
    ' the manufacturer
    Public Sub New()
        ' window title
        Me.Text = "Mon premier formulaire"
        ' window dimensions
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub
 
    ' test function
    Public Shared Sub Main(ByVal args() As String)
        ' the form is displayed
        Application.Run(New Form1)
    End Sub
End Class

O código anterior é compilado e, em seguida, executado

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm1.vb

dos>frm1

A execução apresenta a seguinte janela:

Image

Uma interface gráfica de utilizador deriva geralmente da classe base System.Windows.Forms.Form:

Public Class Form1
    Inherits Form

A classe base Form define uma janela básica com botões para fechar, maximizar/minimizar, tamanho ajustável, etc., e lida com eventos nestes objetos gráficos. Aqui, especializamos a classe base definindo o seu título, largura (300) e altura (100). Isto é feito no seu construtor:


    Public Sub New()
        ' window title
        Me.Text = "Mon premier formulaire"
        ' window dimensions
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

O título da janela é definido pela propriedade Text e as dimensões pela propriedade Size. A propriedade Size está definida no namespace System.Drawing e é uma estrutura. O procedimento Main inicia a aplicação gráfica da seguinte forma:


        Application.Run(New Form1)

É criado e apresentado um formulário do tipo Form1; em seguida, a aplicação aguarda eventos que ocorram no formulário (cliques, movimentos do rato, etc.) e executa aqueles que o formulário processa. Aqui, o nosso formulário não processa quaisquer eventos além daqueles processados pela classe Form base (cliques nos botões Fechar, Maximizar/Minimizar, redimensionamento da janela, movimentação da janela, etc.).

5.1.2. Um Form com um botão

Agora vamos adicionar um botão à nossa janela:


' options
Option Strict On
Option Explicit On 
 
' namespaces
Imports System
Imports System.Drawing
Imports System.Windows.Forms
 
' the form class
Public Class Form1
    Inherits Form
 
    ' attributes
    Private cmdTest As Button
 
    ' the manufacturer
    Public Sub New()
        ' the title
        Me.Text = "Mon premier formulaire"
        ' dimensions
        Me.Size = New System.Drawing.Size(300, 100)
        ' a button
        ' creation
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' size
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' wording
        cmdTest.Text = "Test"
        ' event manager
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' add button to form
        Me.Controls.Add(cmdTest)
    End Sub
 
    ' event manager
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' there was a click on the button - we say it
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub
 
    ' test function
    Public Shared Sub Main(ByVal args() As String)
        ' the form is displayed
        Application.Run(New Form1)
    End Sub
End Class

Adicionámos um botão ao formulário:


        ' un bouton
        ' création
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' taille
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' libellé
        cmdTest.Text = "Test"
        ' gestionnaire d'évt
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' ajout bouton au formulaire
        Me.Controls.Add(cmdTest)

A propriedade Location define as coordenadas (110,20) do canto superior esquerdo do botão utilizando uma estrutura Point. A largura e a altura do botão são definidas como (80,30) utilizando uma estrutura Size. A propriedade Text do botão define o rótulo do botão. A classe Button tem um evento Click definido da seguinte forma:

Public Event Click As EventHandler

onde EventHandler é uma função «delegada» com a seguinte assinatura:

Public Delegate Sub EventHandler(ByVal sender As Object,ByVal e As EventArgs)

Isto significa que o manipulador do evento [Click] no botão deve ter a assinatura do delegado [EventHandler]. Aqui, quando o botão cmdTest for clicado, o método cmdTest_Click será chamado. Este é definido da seguinte forma, de acordo com o modelo EventHandler anterior:


    ' event manager
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' there was a click on the button - we say it
        MessageBox.Show("Clic sur bouton", "Clic sur bouton", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

Simplesmente exibimos uma mensagem:

Image

A classe é compilada e executada:

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4

dos>frm2

A classe MessageBox é utilizada para apresentar mensagens numa janela. Aqui, utilizámos o construtor

Overloads Public Shared Function Show(ByVal owner As IWin32Window,ByVal text As String,ByVal caption As String,ByVal buttons As MessageBoxButtons,ByVal icon As MessageBoxIcon) As DialogResult
text
a mensagem a apresentar
caption
o título da janela
botões
os botões na janela
ícone
o ícone na janela

O parâmetro «botões» pode assumir valores das seguintes constantes:

constante
botões
   AbortarReintentarIgnorar
  OK
    OKCancelar
    RepetirCancelar
    SimNão
    SimNãoCancelar

O parâmetro «icon» pode assumir valores das seguintes constantes:

Asterisco
Erro
igual ao anterior Parar
Exclamação
igual a Aviso
Mão
Informações
igual ao Asterisk
Nenhuma
Pergunta
Parar
O mesmo que Mão
Aviso

 

O método Show é um método estático que devolve um resultado do tipo System.Windows.Forms.DialogResult, que é uma enumeração:


public enum System.Windows.Forms.DialogResult 
{
    Abort = 0x00000003, 
    Cancel = 0x00000002, 
    Ignore = 0x00000005, 
    No = 0x00000007, 
    None = 0x00000000, 
    OK = 0x00000001, 
    Retry = 0x00000004, 
    Yes = 0x00000006, 
}

Para determinar em que botão o utilizador clicou para fechar a janela MessageBox, escrevemos:

dim res as DialogResult=MessageBox.Show(..)
if res=DialogResult.Yes then
' he pressed the yes button
...
end if

5.2. Criação de uma interface gráfica de utilizador com o Visual Studio.NET

Vamos revisitar alguns dos exemplos vistos anteriormente, desta vez criando-os com o Visual Studio.NET.

5.2.1. Criação inicial do projeto

  1. Inicie o VS.NET e selecione Ficheiro/Novo/Projeto

Image

  1. Especifique as características do seu projeto
    1. Selecione o tipo de projeto que pretende criar; neste caso, um projeto VB.NET (1)
    2. Selecione o tipo de aplicação que pretende criar; neste caso, uma aplicação Windows (2)
    3. Especifique a pasta onde pretende colocar a subpasta do projeto (3)
    4. Introduza o nome do projeto (4). Este será também o nome da pasta que irá conter os ficheiros do projeto
    5. O nome desta pasta é apresentado em (5)
  1. São então criadas várias pastas e ficheiros na pasta i4:
subpastas da pasta project1

Desses ficheiros, apenas um é relevante: o ficheiro form1.cs, que é o ficheiro de código-fonte associado ao formulário criado pelo VS.NET. Voltaremos a este assunto mais tarde.

5.2.2. As janelas da interface do VS.NET

A interface do VS.NET apresenta agora determinados elementos do nosso projeto i4:

Temos uma janela de design da interface gráfica do utilizador:

Ao arrastar controlos da barra de ferramentas (Caixa de Ferramentas 2) e soltá-los no painel da janela (1), podemos construir uma interface gráfica de utilizador. Se passarmos o rato sobre a «Caixa de Ferramentas», esta expande-se para revelar vários controlos:

Image

Por enquanto, não vamos usar nenhum deles. Ainda no ecrã do VS.NET, encontramos a janela «Explorer Solution»:

Image

Inicialmente, não vamos utilizar muito esta janela. Ela apresenta todos os ficheiros que compõem o projeto. Estamos interessados apenas num deles: o ficheiro de código-fonte do nosso programa, neste caso Form1.vb. Ao clicar com o botão direito do rato em Form1.vb, surge um menu que permite aceder ao código-fonte da nossa GUI (View Code) ou à própria GUI (Form Designer):

Image

Pode aceder a ambos diretamente a partir da janela «Solution Explorer»:

 

As janelas abertas «empilham-se» na janela de design principal:

Image

Aqui, Form1.vb[Design] refere-se à janela de design e Form1.vb à janela de código. Basta clicar numa das separadores para alternar entre as duas janelas. Outra janela importante apresentada no ecrã do VS.NET é a janela Propriedades:

Image

As propriedades apresentadas na janela são as do controlo atualmente selecionado na janela de design gráfico. Pode aceder a diferentes janelas do projeto através do menu Exibir:

Image

Este inclui as janelas principais acima descritas, juntamente com os respetivos atalhos de teclado.

5.2.3. Executar um projeto

Embora ainda não tenhamos escrito qualquer código, temos um projeto executável. Prima F5 ou Debug/Start para o executar. Aparece a seguinte janela:

Image

Esta janela pode ser maximizada, minimizada, redimensionada e fechada.

5.2.4. O código gerado pelo VS.NET

Vamos ver o código (View/Code) da nossa aplicação:


Public Class Form1
    Inherits System.Windows.Forms.Form
 
#Region " Code généré par le Concepteur Windows Form "
 
    Public Sub New()
        MyBase.New()
 
        'This call is required by the Windows Form Designer.
        InitializeComponent()
 
        'Add any initialization after InitializeComponent() call
 
    End Sub
 
    'The substituted method Disposes of the form to clean up the list of components.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub
 
    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer
 
    'REMARQUE: the following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.Text = "Form2"
    End Sub
 
#End Region
 
End Class

Uma interface gráfica de utilizador deriva da classe base System.Windows.Forms.Form:

Public Class Form1
    Inherits System.Windows.Forms.Form

A classe base Form define uma janela básica com botões para fechar e maximizar/minimizar, tamanho ajustável e muito mais, e lida com eventos nestes objetos gráficos. O construtor do formulário utiliza um método InitializeComponent no qual os controlos do formulário são criados e inicializados.


    Public Sub New()
        MyBase.New()
 
        'This call is required by the Windows Form Designer.
        InitializeComponent()
 
        'Add any initialization after InitializeComponent() call
    End Sub

Qualquer outra tarefa a ser realizada no construtor pode ser feita após a chamada a InitializeComponent. O método InitializeComponent


Private Sub InitializeComponent()
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 53)
        Me.Name = "Form1"
        Me.Text = "Form1"
    End Sub

define o título da janela "Form1", a sua largura (292) e a sua altura (53). O título da janela é definido pela propriedade Text e as dimensões pela propriedade Size. Size é definida no namespace System.Drawing e é uma estrutura. Para executar esta aplicação, precisamos de definir o módulo principal do projeto. Para tal, utilizamos a opção [Projects/Properties]:

Image

Em [Startup Object], especificamos [Form1], que é o formulário que acabámos de criar. Para executar a aplicação, utilizamos a opção [Debug/Start]:

Image

5.2.5. Compilação numa janela do Prompt de Comando

Agora, vamos tentar compilar e executar esta aplicação numa janela do DOS:

dos>dir form1.vb
14/03/2004  11:53               514 Form1.vb

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

vbc : error BC30420: 'Sub Main' cannot be found in 'Form1'.

O compilador indica que não consegue encontrar o procedimento [Main]. De facto, o VS.NET não o gerou. No entanto, já o encontrámos nos exemplos anteriores. Tem a seguinte forma:


    Shared Sub Main()
        ' launch application
        Application.Run(New Form1) ' where Form1 is the form
    End Sub

Vamos adicionar o código anterior ao [Form1.vb] e recompilar:

dos>vbc /r:system.dll /r:system.windows.forms.dll form2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4

Form2.vb(41) : error BC30451: Le nom 'Application' n'est pas déclaré.

        Application.Run(New Form2)
        ~~~~~~~~~~~

Desta vez, o nome [Application] é desconhecido. Isto significa simplesmente que ainda não importámos o seu namespace [System.Windows.Forms]. Vamos adicionar a seguinte instrução:

Imports System.Windows.Forms

e, em seguida, recompilemos:

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

Desta vez, funciona. Vamos executá-lo:

dos>dir
14/03/2004  11:53               514 Form1.vb
14/03/2004  12:15             3 584 Form1.exe

dos>form1

Image

É criado e apresentado um formulário do tipo Form1. Pode evitar adicionar o procedimento [Main] utilizando a opção /m do compilador, que permite especificar a classe a executar se esta herdar de System.Windows.Forms:

dos>vbc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /m:form2 form2.vb

A opção /m:form2 especifica que a classe a ser executada é a classe denominada [form2].

5.2.6. Tratamento de eventos

Assim que o formulário é apresentado, a aplicação fica à espera de eventos que ocorram no formulário (cliques, movimentos do rato, etc.) e executa aqueles que o formulário trata. Aqui, o nosso formulário não trata de quaisquer eventos além daqueles tratados pela classe Form base (cliques nos botões de fechar, botões de maximizar/minimizar, redimensionamento da janela, movimento da janela, etc.). O formulário gerado utiliza um atributo components que não é utilizado em lado nenhum. O método Dispose também é desnecessário aqui. O mesmo se aplica a determinados namespaces (Collections, ComponentModel, Data) que são utilizados e ao definido para o projeto projet1. Portanto, neste exemplo, o código pode ser simplificado para o seguinte:


Imports System
Imports System.Drawing
Imports System.Windows.Forms
 
Public Class Form1
    Inherits System.Windows.Forms.Form
 
    ' manufacturer
    Public Sub New()
        ' building the form with its components
        InitializeComponent()
    End Sub
 
    Private Sub InitializeComponent()
        ' window size
        Me.Size = New System.Drawing.Size(300, 300)
        ' window title
        Me.Text = "Form1"
    End Sub
 
    Shared Sub Main()
        ' launch application
        Application.Run(New Form1)
    End Sub
End Class

5.2.7. Conclusão

Vamos agora aceitar o código gerado pelo VS.NET tal como está e simplesmente adicionar o nosso próprio código, especificamente para tratar de eventos relacionados com os vários controlos no formulário.

5.3. Janela com campo de entrada, botão e rótulo

5.3.1. Design gráfico

No exemplo anterior, não colocámos quaisquer componentes na janela. Vamos iniciar um novo projeto chamado interface2. Para tal, seguiremos o procedimento descrito anteriormente para criar um projeto:

Image

Agora vamos criar uma janela com um botão, um rótulo e um campo de entrada:

Os campos são os seguintes:

N.º
nome
Tipo
função
1
lblInput
Rótulo
um rótulo
2
txtInput
Caixa de Texto
um campo de entrada
3
btnDisplay
Botão
para exibir o conteúdo da caixa de texto txtSaisie numa caixa de diálogo

Pode proceder da seguinte forma para criar esta janela: clique com o botão direito do rato dentro da janela, fora de qualquer componente, e selecione a opção Propriedades para aceder às propriedades da janela:

Image

A janela Propriedades aparece então à direita:

Image

Algumas destas propriedades merecem destaque:

Cor de fundo
para definir a cor de fundo da janela
ForeColor
para definir a cor dos gráficos ou do texto na janela
Menu
para associar um menu à janela
Texto
para atribuir um título à janela
FormBorderStyle
para definir o tipo de janela
Font
para definir o tipo de letra do texto na janela
Nome
para definir o nome da janela

Aqui, definimos as propriedades Text e Name:

Texto
Fechos e botões - 1
Nome
frmInputButtons

Utilizar a barra «Caixa de ferramentas»

  • selecione os componentes de que necessita
  • arraste-os para a janela e defina as suas dimensões corretas
Depois de selecionar o componente na "caixa de ferramentas", use a
tecla «Esc» para ocultar a barra de ferramentas e, em seguida, solte
e redimensione o componente. Faça isso para os três
necessários: Label, TextBox, Button. Para alinhar e
dimensionar os componentes corretamente, use o menu Formatar:
O processo de formatação funciona da seguinte forma:
  1. selecione os vários componentes a formatar em conjunto (mantenha premida a tecla Ctrl)
  2. selecione a opção de formatação desejada
A opção Alinhar permite-lhe alinhar os componentes
A opção «Tamanho igual» garante que
os componentes tenham a mesma altura ou a mesma
largura:
A opção Espaçamento horizontal, por exemplo,
alinhar os componentes horizontalmente com
espaçamento igual entre eles. O mesmo se aplica
à opção Espaçamento vertical para alinhar verticalmente.
A opção «Centralizar no formulário» permite-lhe centralizar
um componente na horizontal ou
verticalmente dentro da janela:
Depois de os componentes estarem corretamente
colocados na janela, defina as suas propriedades.
Para isso, clique com o botão direito do rato no componente e
selecione a opção Propriedades:
  • Rótulo 1
Selecione o componente para abrir
a janela de Propriedades. Nesta janela,
altere as seguintes propriedades:
nome: lblSaisie, texto: Saisie
  • Campo de entrada 2 (TextBox)
Selecione o componente para abrir a sua
janela de propriedades. Nesta janela, modifique
as seguintes propriedades:
nome: txtSaisie, texto: deixe em branco
  • Botão 3 (Botão):
nome: cmdAfficher, texto: Exibir
  • a própria janela: nome: frmSaisies&Boutons,
texto: Entradas e botões - 1
Podemos executar (Ctrl-F5) o nosso projeto
para ter uma primeira visão da janela
em ação:

Feche a janela. Ainda precisamos de escrever o procedimento associado a um clique no botão Exibir.

5.3.2. Tratamento de eventos do formulário

Vejamos o código gerado pelo designer visual:


...
 
Public Class frmSaisiesBoutons
  Inherits System.Windows.Forms.Form
 
  ' components
    Private components As System.ComponentModel.Container = Nothing
    Friend WithEvents btnAfficher As System.Windows.Forms.Button
    Friend WithEvents lblsaisie As System.Windows.Forms.Label
    Friend WithEvents txtsaisie As System.Windows.Forms.TextBox
 
  ' manufacturer
  Public Sub New()
    InitializeComponent()
  End Sub
 
...
 
  ' component initialization
    Private Sub InitializeComponent()
...
    End Sub
End Class
 

Primeiro, repare na declaração específica dos componentes:

  • a palavra-chave Friend indica que o componente é visível para todas as classes do projeto
  • a palavra-chave WithEvents indica que o componente gera eventos. Vamos agora ver como lidar com esses eventos

Exiba a janela de código do formulário (View/Code ou F7):

A janela acima apresenta duas listas suspensas (1) e (2). A lista (1) é a lista de componentes do formulário:

Image

A lista (2) mostra os eventos associados ao componente selecionado em (1):

Image

Um dos eventos associados ao componente é apresentado a negrito (neste caso, Click). Este é o evento predefinido do componente. Pode aceder ao manipulador deste evento específico clicando duas vezes no componente na janela de design. O VB.NET gera então automaticamente o esqueleto do manipulador de eventos na janela de código e posiciona o cursor sobre ele. Para aceder aos manipuladores dos outros eventos, vá para a janela de código, selecione o componente na lista (1) e selecione o evento em (2). O VB.NET gera então o esqueleto do manipulador de eventos ou posiciona o cursor sobre ele, caso já tenha sido gerado:


    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

Por predefinição, o VB.NET atribui um nome ao manipulador de eventos para o evento E do componente C. Pode alterar este nome, se desejar. No entanto, isso não é recomendado. Os programadores VB geralmente mantêm o nome gerado pelo VB, o que garante a consistência na nomenclatura em todos os programas VB. A associação do procedimento btnAfficher_Click ao evento Click do componente btnAfficher não é feita através do nome do procedimento, mas sim através da palavra-chave handles:


Handles btnAfficher.Click

No código acima, a palavra-chave handles especifica que o procedimento trata do evento Click do componente btnAfficher. O manipulador de eventos tem dois parâmetros:

sender
o objeto que desencadeou o evento (neste caso, o botão)
e
um objeto EventArgs que detalha o evento que ocorreu

Não vamos utilizar nenhum destes parâmetros aqui. Resta apenas completar o código. Aqui, queremos apresentar uma caixa de diálogo com o conteúdo do campo txtSaisie:


    Private Sub btnAfficher_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' the text entered in the TxtSaisie input box is displayed
        MessageBox.Show("texte saisi= " + txtsaisie.Text, "Vérification de la saisie", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

Quando a aplicação é executada, ocorre o seguinte:

Image

5.3.3. Outro método para lidar com eventos

Para o botão btnAfficher, o VS.NET gerou o seguinte código:


Private Sub InitializeComponent()
        ...
        '
        'btnAfficher
        '
        Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
        Me.btnAfficher.Name = "btnAfficher"
        Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
        Me.btnAfficher.TabIndex = 2
        Me.btnAfficher.Text = "Afficher"
...
    End Sub
 
...
 
    ' event manager click on cmdAfficher button
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

Podemos associar o procedimento btnAfficher_Click ao evento Click do botão btnAfficher de outra forma:


Private Sub InitializeComponent()
        ...
        '
        'btnAfficher
        '
        Me.btnAfficher.Location = New System.Drawing.Point(102, 48)
        Me.btnAfficher.Name = "btnAfficher"
        Me.btnAfficher.Size = New System.Drawing.Size(88, 24)
        Me.btnAfficher.TabIndex = 2
        Me.btnAfficher.Text = "Afficher"
        AddHandler btnAfficher.Click, AddressOf btnAfficher_Click
...
    End Sub
 
...
 
    ' event manager click on cmdAfficher button
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
    End Sub

O procedimento btnAfficher_Click perdeu a palavra-chave Handles, perdendo assim a sua associação com o evento btnAfficher.Click. Esta associação é agora feita utilizando a palavra-chave AddHandler:


        AddHandler btnAfficher.Click, AddressOf btnAfficher_Click

O código acima, que será colocado no procedimento InitializeComponent do formulário, associa o procedimento denominado btnAfficher_Click ao evento btnAfficher.Click. Além disso, o componente btnAfficher já não requer a palavra-chave WithEvents:


    Friend btnAfficher As System.Windows.Forms.Button

Qual é a diferença entre os dois métodos?

  • A palavra-chave handles permite apenas que um evento seja associado a um procedimento em tempo de design. O designer sabe antecipadamente que um procedimento P deve tratar os eventos E1, E2, ... e escreve o código

    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) handles E1, E2, ..., En

É, de facto, possível que um procedimento trate de vários eventos.

  • A palavra-chave addhandler permite que um evento seja associado a um procedimento em tempo de execução. Isto é útil numa estrutura de eventos produtor-consumidor. Um objeto produz um evento específico que pode ser do interesse de outros objetos. Estes objetos subscrevem o produtor para receber o evento (uma temperatura que exceda um limiar crítico, por exemplo). Durante a execução da aplicação, o produtor do evento terá de executar várias instruções:
Addhandler E, AddressOf P1
Addhandler E, AddressOf P2
...
Addhandler E, AddressOf Pn

onde E é o evento produzido pelo produtor e P1 é um dos procedimentos pertencentes aos vários objetos que consomem este evento. Teremos a oportunidade de revisitar uma aplicação de eventos produtor-consumidor num capítulo posterior.

5.3.4. Conclusão

A partir dos dois projetos estudados, podemos concluir que, uma vez construída a interface gráfica do utilizador com o VS.NET, a tarefa do programador é escrever os manipuladores de eventos para os eventos que deseja gerir dentro dessa interface. A partir de agora, apresentaremos apenas o código para estes manipuladores.

5.4. Alguns Componentes Úteis

Apresentaremos agora várias aplicações que utilizam os componentes mais comuns para explorar os seus principais métodos e propriedades. Para cada aplicação, apresentaremos a interface gráfica do utilizador e o código relevante, em particular os manipuladores de eventos.

5.4.1. Formulário

Começaremos por apresentar o componente essencial: o formulário no qual os componentes são colocados. Já abordámos algumas das suas propriedades básicas. Aqui, iremos concentrar-nos em alguns eventos importantes do formulário.

 
O formulário está a carregar
Fechar
O formulário está a fechar
Fechado
O formulário está fechado

O evento Load ocorre antes mesmo de o formulário ser apresentado. O evento Closing ocorre quando o formulário está a ser fechado. Este encerramento ainda pode ser interrompido programaticamente. Criamos um formulário chamado Form1 sem quaisquer componentes:

Image

Tratamos os três eventos anteriores:


    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' initial form loading
        MessageBox.Show("Evt Load", "Load")
    End Sub
 
    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        ' the form is closing
        MessageBox.Show("Evt Closing", "Closing")
        ' confirmation requested
        Dim réponse As DialogResult = MessageBox.Show("Voulez-vous vraiment quitter l'application", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If réponse = DialogResult.No Then
            e.Cancel = True
        End If
    End Sub
 
    Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
        ' the form is closing
        MessageBox.Show("Evt Closed", "Closed")
    End Sub
Utilizamos a função MessageBox para sermos notificados dos vários
eventos. O evento de encerramento ocorre quando o utilizador
fecha a janela.
Em seguida, perguntamos se o utilizador deseja realmente sair da aplicação:
Se responderem Não, definimos a propriedade Cancel do
CancelEventArgs que o método recebeu como parâmetro.
Se definirmos esta propriedade como False, o
fecho da janela é cancelado; caso contrário, o processo continua:

5.4.2. Controlos Label e TextBox

Já nos deparámos com estes dois componentes. O Label é um componente de texto e o TextBox é um componente de campo de entrada. A sua principal propriedade é Text, que se refere ao conteúdo do campo de entrada ou ao texto do rótulo. Esta propriedade é de leitura/gravação. O evento normalmente utilizado para o TextBox é o TextChanged, que sinaliza que o utilizador modificou o campo de entrada. Aqui está um exemplo que utiliza o evento TextChanged para acompanhar as alterações num campo de entrada:

N.º
tipo
nome
função
1
Caixa de Texto
txtInput
campo de entrada
2
Rótulo
lblControl
exibe o texto de 1 em tempo real
3
Botão
cmdClear
para limpar os campos 1 e 2
4
Botão
cmdExit
para sair da aplicação

O código relevante para esta aplicação são os manipuladores de eventos:


  ' click on btn quit
    Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdQuitter.Click
        ' click on the Quit button - exit the application
        Application.Exit()
    End Sub
 
    ' field modification txtSaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' the content of TextBox has changed - copy it to Label lblControle
        lblControle.Text = txtSaisie.Text
    End Sub
 
    ' click on btn delete
    Private Sub cmdEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdEffacer.Click
        ' delete the contents of the input box
        txtSaisie.Text = ""
    End Sub
 
    ' a key has been pressed
    Private Sub txtSaisie_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
    Handles txtSaisie.KeyPress
        ' see which key has been pressed
        Dim touche As Char = e.KeyChar
        If touche = ControlChars.Cr Then
            MessageBox.Show(txtSaisie.Text, "Contrôle", MessageBoxButtons.OK, MessageBoxIcon.Information)
            e.Handled = True
        End If
    End Sub

Observe como a aplicação é encerrada no procedimento cmdQuitter_Click: Application.Exit(). O exemplo seguinte utiliza uma caixa de texto com várias linhas:

A lista de controlos é a seguinte:

N.º
Tipo
nome
função
1
Caixa de Texto
txtMultiLines
campo de entrada multilinha
2
Caixa de Texto
txtAdd
campo de entrada de linha única
3
Botão
btnAdd
Subtrair 2 de 1

Para tornar uma caixa de texto multilinha, defina as seguintes propriedades do controlo:

 
para permitir várias linhas de texto
ScrollBars=(None, Horizontal, Vertical, Both)
para especificar se o controlo deve ter barras de deslocamento (Horizontal, Vertical, Both) ou não (None)
AcceptReturn=(True, False)
se definido como verdadeiro, a tecla Enter irá passar para a linha seguinte
AceitarTab=(Verdadeiro, Falso)
se definido como verdadeiro, a tecla Tab irá inserir uma tabulação no texto

O código relevante é aquele que trata do clique no botão [Add] e aquele que trata das alterações no campo de entrada [txtAdd]:


    ' évt btnAjouter_Click
    Private Sub btnAjouter_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' add the content of txtAjout to that of txtMultilignes
        txtMultilignes.Text &= txtAjout.Text
        txtAjout.Text = ""
    End Sub
 
    ' evt txtAjout_TextChanged
    Private Sub txtAjout_TextChanged1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtAjout.TextChanged
        ' set the state of the Add button
        btnAjouter.Enabled = txtAjout.Text.Trim() <> ""
    End Sub

5.4.3. Listas suspensas do ComboBox

Um componente ComboBox é uma lista suspensa combinada com um campo de entrada: o utilizador pode selecionar um item (2) ou introduzir texto (1). Existem três tipos de ComboBox definidos pela propriedade Style:

 
lista não suspensa com caixa de edição
DropDown
lista suspensa com caixa de edição
Lista suspensa
lista suspensa sem caixa de edição

Por predefinição, o tipo de uma ComboBox é DropDown. Para saber mais sobre a classe ComboBox, digite ComboBox no índice da Ajuda (Ajuda/Índice). A classe ComboBox tem um único construtor:

Public Sub New()
cria um objeto ComboBox vazio

Os itens no ComboBox estão disponíveis na propriedade Items:

Public ReadOnly Property Items As ComboBox.ObjectCollection

Esta é uma propriedade indexada, em que Items(i) se refere ao i-ésimo item na Combo. Seja C uma Combo e C.Items a sua lista de itens. Temos as seguintes propriedades:

 
número de itens na ComboBox
C.Items(i)
elemento i do ComboBox
C.Add(object o)
adiciona o objeto o como o último elemento da lista suspensa
C.AddRange(object() objects)
adiciona uma matriz de objetos ao final da lista suspensa
C.Insert(int i, object o)
adiciona o objeto o na posição i no combo
C.RemoveAt(int i)
remove o elemento i da caixa de combinação
C.Remove(object o)
remove o objeto o da caixa de combinação
C.Clear()
limpa todos os itens da caixa de combinação
C.IndexOf(objeto o)
retorna a posição i do objeto o na caixa de combinação

Pode parecer surpreendente que uma caixa de combinação possa conter objetos, quando normalmente contém cadeias de caracteres. Visualmente, é de facto esse o caso. Se uma ComboBox contiver um objeto obj, ela exibe a cadeia de caracteres obj.ToString(). Recorde-se que todos os objetos têm um método ToString herdado da classe Object, que retorna uma cadeia de caracteres que «representa» o objeto. O item selecionado na caixa de combinação C é C.SelectedItem ou C.Items(C.SelectedIndex), onde C.SelectedIndex é o índice do item selecionado, começando por zero para o primeiro item.

Quando um item é selecionado da lista suspensa, o evento SelectedIndexChanged é acionado, o que pode então ser usado para detectar uma alteração na seleção da caixa de combinação. Na aplicação a seguir, usamos este evento para exibir o item que foi selecionado da lista.

Image

Estamos a mostrar apenas o código relevante para a janela. No construtor do formulário, preenchemos a caixa de combinação:


  Public Sub New()
    ' création formulaire
    InitializeComponent()
    ' remplissage combo
    cmbNombres.Items.AddRange(New String() {"zéro", "un", "deux", "trois", "quatre"})
        ' nous sélectionnons le 1er élément de la liste
    cmbNombres.SelectedIndex = 0
  End Sub

Tratamos o evento SelectedIndexChanged da lista suspensa, que indica que um novo item foi selecionado:


    Private Sub cmbNombres_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmbNombres.SelectedIndexChanged
        ' the selected item has changed - it is displayed
        MessageBox.Show("Elément sélectionné : (" & cmbNombres.SelectedItem.ToString & "," & cmbNombres.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

5.4.4. Componente ListBox

Propomos criar a seguinte interface:

Os componentes desta janela são os seguintes:

N.º
Tipo
nome
função/propriedades
0
Formulário
Form1
formulário - BorderStyle=FixedSingle
1
Caixa de Texto
txtInput
campo de entrada
2
Botão
btnAdd
botão para adicionar o conteúdo do campo de entrada 1 à lista 3
3
ListBox
listBox1
Lista 1
4
Caixa de Lista
listBox2
lista 2
5
Botão
btn1TO2
transfere os itens selecionados da lista 1 para a lista 2
6
Botão
cmd2T0
faz o contrário
7
Botão
btnClear1
limpa a lista 1
8
Botão
btnClear2
limpa a lista 2
  • O utilizador digita texto no campo 1. Adiciona-o à lista 1 utilizando o botão Adicionar (2). O campo de entrada (1) é então limpo, e o utilizador pode adicionar um novo item.
  • Pode transferir itens de uma lista para outra selecionando o item a transferir numa das listas e escolhendo o botão de transferência apropriado 5 ou 6. O item transferido é adicionado ao final da lista de destino e removido da lista de origem.
  • Pode clicar duas vezes num item da Lista 1. Este item é então transferido para a caixa de entrada para edição e removido da Lista 1.

Os botões são ativados ou desativados de acordo com as seguintes regras:

  • o botão Adicionar só fica ativado se houver texto no campo de entrada
  • O botão 5 para transferir da Lista 1 para a Lista 2 só fica ativado se um item estiver selecionado na Lista 1
  • O botão 6, para transferir da Lista 2 para a Lista 1, só fica ativo se houver um item selecionado na Lista 2
  • Os botões 7 e 8 para limpar as listas 1 e 2 só ficam ativos se a lista a ser limpa contiver itens.

Nas condições acima, todos os botões devem estar desativados quando a aplicação for iniciada. Isto significa que a propriedade Enabled dos botões deve ser definida como false. Isto pode ser feito durante o tempo de design, o que irá gerar o código correspondente no método InitializeComponent, ou podemos fazê-lo nós próprios no construtor, conforme mostrado abaixo:


    Public Sub New()
        ' initial form creation
        InitializeComponent()
        ' additional initializations
        ' a number of buttons are disabled
        btnAjouter.Enabled = False
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
        btnEffacer1.Enabled = False
        btnEffacer2.Enabled = False
    End Sub

O estado do botão Adicionar é controlado pelo conteúdo do campo de entrada de texto. O evento TextChanged permite-nos acompanhar as alterações a este conteúdo:


    ' change in field txtsaisie
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' the content of txtSaisie has changed
        ' the Add button is only lit if the entry is non-empty
        btnAjouter.Enabled = txtSaisie.Text.Trim() <> ""
    End Sub

O estado dos botões de transferência depende de ter ou não sido selecionado um item na lista que controlam:


    ' chgt selected item without listbox1
    Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox1.SelectedIndexChanged
 
        ' an item has been selected
        ' switch on the 1 to 2 transfer button
        btn1TO2.Enabled = True
    End Sub
 
    ' chgt selected item without listbox2
    Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox2.SelectedIndexChanged
        ' an item has been selected
        ' switch on the 2 to 1 transfer button
        btn2TO1.Enabled = True
    End Sub

O código associado ao clique no botão Adicionar é o seguinte:


    ' click on btn Add
    Private Sub btnAjouter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAjouter.Click
        ' add a new element to list 1
        listBox1.Items.Add(txtSaisie.Text.Trim())
        ' raz de la saisie
        txtSaisie.Text = ""
        ' List 1 is not empty
        btnEffacer1.Enabled = True
        ' return focus to input box
        txtSaisie.Focus()
    End Sub

Repare no método Focus, que permite definir o «foco» num controlo do formulário. O código associado ao clique nos botões Limpar:


    ' click on btn Delete1
    Private Sub btnEffacer1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer1.Click
        ' delete list 1
        listBox1.Items.Clear()
        btnEffacer1.Enabled = False
    End Sub
 
    ' click on btn delete2
    Private Sub btnEffacer2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' delete list 2
        listBox2.Items.Clear()
        btnEffacer2.Enabled = False
    End Sub

O código para transferir os itens selecionados de uma lista para outra:


    ' click on btn btn1to2
    Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn1TO2.Click
        ' transfer the item selected in List 1 to List 2
        transfert(listBox1, listBox2)
        ' delete buttons
        btnEffacer2.Enabled = True
        btnEffacer1.Enabled = listBox1.Items.Count <> 0
        ' transfer buttons
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub
 
    ' click on btn btn2to1
    Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn2TO1.Click
        ' transfer item selected in List 2 to List 1
        transfert(listBox2, listBox1)
        ' delete buttons
        btnEffacer1.Enabled = True
        btnEffacer2.Enabled = listBox2.Items.Count <> 0
        ' transfer buttons
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' transfer
    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)
        ' transfer selected item from list 1 to list l2
        ' a selected item?
        If l1.SelectedIndex = -1 Then Return
        ' addition to l2
        l2.Items.Add(l1.SelectedItem)
        ' deletion in l1
        l1.Items.RemoveAt(l1.SelectedIndex)
    End Sub

Primeiro, criamos um método


    Private Sub transfert(ByVal l1 As ListBox, ByVal l2 As ListBox)

que transfere o item selecionado da lista l1 para a lista l2. Isto permite-nos utilizar um único método em vez de dois para transferir um item da ListBox1 para a ListBox2 ou da ListBox2 para a ListBox1. Antes de realizar a transferência, certificamo-nos de que existe efetivamente um item selecionado na lista l1:


        ' un élément sélectionné ?
        If l1.SelectedIndex = -1 Then Return

A propriedade SelectedIndex é -1 se nenhum item estiver selecionado no momento. Nos procedimentos


    Private Sub btnXTOY_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnXTOY.Click

transferimos o conteúdo da lista X para a lista Y e atualizamos o estado de determinados controlos para refletir o novo estado das listas.

5.4.5. Caixas de seleção CheckBox, botões de opção ButtonRadio

Propomos escrever a seguinte aplicação:

Os componentes da janela são os seguintes:

N.º
Tipo
nome
função
1
Botão de opção
radioButton1
botão de opção 2
radioButton3
3 botões de opção
2
Caixa de seleção
caixa de seleção 1
caixa de seleção 2
caixa de seleção 3
3 caixas de seleção
3
Caixa de lista
lstValues
uma lista

Se criarmos os três botões de opção um após o outro, eles fazem parte do mesmo grupo por predefinição. Portanto, quando um é selecionado, os outros não o são. O evento que nos interessa para estes seis controlos é o evento CheckChanged, que indica que o estado da caixa de seleção ou do botão de opção mudou. Este estado é representado em ambos os casos pela propriedade booleana Check, que, quando verdadeira, significa que o controlo está marcado. Aqui, utilizámos um único método para tratar todos os seis eventos CheckChanged; o método exibe:


  ' poster
    Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles checkBox1.CheckedChanged, checkBox2.CheckedChanged, checkBox3.CheckedChanged, _
    radioButton1.CheckedChanged, radioButton2.CheckedChanged, radioButton3.CheckedChanged
        ' displays radio button or checkbox status
        ' is this a checkbox?
        If (TypeOf (sender) Is CheckBox) Then
            Dim chk As CheckBox = CType(sender, CheckBox)
            lstValeurs.Items.Insert(0, chk.Name & "=" & chk.Checked)
        End If
        ' is it a radiobutton?
        If (TypeOf (sender) Is RadioButton) Then
            Dim rdb As RadioButton = CType(sender, RadioButton)
            lstValeurs.Items.Insert(0, rdb.Name & "=" & rdb.Checked)
        End If
    End Sub

A sintaxe TypeOf (sender) Is CheckBox permite-nos verificar se o objeto remetente é do tipo CheckBox. Isto permite-nos, então, converter o remetente para o tipo exato. O método apresenta o nome do componente que desencadeou o evento e o valor da sua propriedade Checked na lista lstValeurs. Em tempo de execução, verificamos que clicar num botão de opção desencadeia dois eventos CheckChanged: um no botão anteriormente marcado, que passa a ficar «desmarcado», e outro no novo botão, que passa a ficar «marcado».

5.4.6. Controlos ScrollBar

Existem vários tipos de barras de rolagem: a barra de rolagem horizontal (hScrollBar),
a barra de rolagem vertical (vScrollBar) e o controlo numérico para cima/para baixo (NumericUpDown).

Vamos criar a seguinte aplicação:

N.º
tipo
nome
função
1
hScrollBar
hScrollBar1
uma barra de deslocamento horizontal
2
hScrollBar
hScrollBar2
um controlo deslizante horizontal que acompanha as alterações no controlo deslizante 1
3
TextBox
txtValue
exibe o valor do controlo deslizante horizontal
ReadOnly=true para impedir qualquer entrada
4
NumericUpDown
incrementador
permite definir o valor do controlo deslizante 2
  • Um controlo deslizante ScrollBar permite ao utilizador selecionar um valor a partir de um intervalo de valores inteiros representado pela «faixa» do controlo deslizante, ao longo da qual um cursor se move. O valor do controlo deslizante está disponível na sua propriedade Value.
  • Para um controlo deslizante horizontal, a extremidade esquerda representa o valor mínimo do intervalo, a extremidade direita o valor máximo e o cursor o valor atualmente selecionado. Para um controlo deslizante vertical, o mínimo é representado pela extremidade superior e o máximo pela extremidade inferior. Estes valores são representados pelas propriedades Minimum e Maximum e têm como valores predefinidos 0 e 100.
  • Clicar nas extremidades do controlo deslizante altera o valor num incremento (positivo ou negativo) com base na extremidade clicada, designado por SmallChange, cujo valor predefinido é 1.
  • Clicar em qualquer um dos lados do controlo deslizante altera o valor em um incremento (positivo ou negativo), dependendo da extremidade clicada, uma configuração chamada LargeChange, cujo valor padrão é 10.
  • Quando clica na extremidade superior de um controlo deslizante vertical, o seu valor diminui. Isto pode surpreender o utilizador comum, que normalmente espera ver o valor «aumentar». Pode resolver este problema definindo as propriedades SmallChange e LargeChange para valores negativos
  • Estas cinco propriedades (Value, Minimum, Maximum, SmallChange, LargeChange) estão acessíveis tanto para leitura como para escrita.
  • O evento principal do controlador deslizante é aquele que sinaliza uma alteração no valor: o evento Scroll.

Um componente NumericUpDown é semelhante a um controlo deslizante: também possui propriedades Minimum, Maximum e Value, com valores predefinidos de 0, 100 e 0, respetivamente. No entanto, neste caso, a propriedade Value é apresentada numa caixa de entrada que é parte integrante do controlo. O utilizador pode modificar este valor por si próprio, a menos que a propriedade ReadOnly do controlo tenha sido definida como true. O valor de incremento é definido pela propriedade Increment, cujo valor padrão é 1. O evento principal do componente NumericUpDown é aquele que sinaliza uma alteração de valor: o evento ValueChanged. O código relevante para a nossa aplicação é o seguinte:

O formulário é formatado durante a sua criação:


    ' manufacturer
    Public Sub New()
        ' initial form creation
        InitializeComponent()
        ' drive 2 is given the same characteristics as drive 1
        hScrollBar2.Minimum = hScrollBar1.Value
        hScrollBar2.Minimum = hScrollBar1.Minimum
        hScrollBar2.Maximum = hScrollBar1.Maximum
        hScrollBar2.LargeChange = hScrollBar1.LargeChange
        hScrollBar2.SmallChange = hScrollBar1.SmallChange
        ' ditto for the incrementer
        incrémenteur.Minimum = hScrollBar1.Value
        incrémenteur.Minimum = hScrollBar1.Minimum
        incrémenteur.Maximum = hScrollBar1.Maximum
        incrémenteur.Increment = hScrollBar1.SmallChange
        ' give TextBox the value of drive 1
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

O manipulador que monitoriza as alterações no valor do controlo deslizante 1:


    ' hscrollbar1 drive management
    Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar1.Scroll
        ' value change on drive 1
        ' its value is passed on to drive 2 and the TxtValeur textbox
        hScrollBar2.Value = hScrollBar1.Value
        txtValeur.Text = "" & hScrollBar1.Value
    End Sub

O manipulador que monitoriza as alterações no valor do controlo deslizante 2:


    ' hscrollbar2 drive management
    Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar2.Scroll
        ' inhibits all changes to drive 2
        ' forcing it to keep the value of drive 1
        e.NewValue = hScrollBar1.Value
    End Sub

O manipulador que monitoriza as alterações na barra de deslocamento:


    ' incremental management
    Private Sub incrémenteur_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles incrémenteur.ValueChanged
        ' set the value of controller 2
        hScrollBar2.Value = CType(incrémenteur.Value, Integer)
    End Sub

5.5. Eventos do rato

Ao desenhar num contentor, é importante saber a posição do rato, por exemplo, para exibir um ponto quando clicado. Os movimentos do rato desencadeiam eventos no contentor dentro do qual o rato se move.

 
O rato acabou de entrar na área do controlo
MouseLeave
O rato acabou de sair da área do controlo
MouseMove
O rato está a mover-se dentro da área do controlo
MouseDown
Botão esquerdo do rato pressionado
MouseUp
Botão esquerdo do rato solto
Arrastar e soltar
O utilizador solta um objeto no controlo
Arrastar e entrar
O utilizador entra na área do controlo enquanto arrasta um objeto
DragLeave
O utilizador sai da área do controlo enquanto arrasta um objeto
DragOver
O utilizador move-se sobre a área do controlo enquanto arrasta um objeto

Aqui está um programa para o ajudar a compreender melhor quando ocorrem os vários eventos do rato:

N.º
tipo
nome
função
1
Rótulo
lblPosition
para exibir a posição do rato no formulário 1, na lista 2 ou no botão 3
2
ListBox
lstEvts
para exibir eventos do rato que não sejam MouseMove
3
Botão
btnClear
para limpar o conteúdo de 2

Os manipuladores de eventos são os seguintes. Para acompanhar os movimentos do rato nos três controlos, escrevemos um único manipulador:


    ' évt form1_mousemove
    Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseMove, lstEvts.MouseMove, btnEffacer.MouseMove, lstEvts.MouseMove
        ' mvt mouse - displays its (X,Y) coordinates
        lblPosition.Text = "(" & e.X & "," & e.Y & ")"
    End Sub
 

É importante notar que, sempre que o rato entra na área de um controlo, o seu sistema de coordenadas muda. A sua origem (0,0) é o canto superior esquerdo do controlo em que se encontra atualmente. Assim, durante a execução, ao mover o rato do formulário para o botão, a mudança nas coordenadas é claramente visível. Para ver melhor estas mudanças no âmbito do rato, pode utilizar a propriedade Cursor dos controlos:

Image

Esta propriedade permite definir a forma do cursor do rato quando este entra na área do controlo. Assim, no nosso exemplo, definimos o cursor como Padrão para o próprio formulário, Mão para a Lista 2 e Não para o Botão 3, conforme mostrado nas capturas de ecrã abaixo.

Image

Image

Image

No método [InitializeComponent], o código gerado por estas opções é o seguinte:


        Me.lstEvts.Cursor = System.Windows.Forms.Cursors.Hand
        Me.btnEffacer.Cursor = System.Windows.Forms.Cursors.No

Além disso, para detetar entradas e saídas do rato na Lista 2, tratamos os eventos MouseEnter e MouseLeave para essa lista:


    ' evt lstEvts_MouseEnter
    Private Sub lstEvts_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles lstEvts.MouseEnter
        affiche("MouseEnter sur liste")
    End Sub
 
    ' evt lstEvts_MouseLeave
    Private Sub lstEvts_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles lstEvts.MouseLeave
        affiche("MouseLeave sur liste")
    End Sub
    
' poster
    Private Sub affiche(ByVal message As String)
        ' the message is displayed at the top of the event list
        lstEvts.Items.Insert(0, message)
    End Sub

Image

Para processar os cliques no formulário, tratamos os eventos MouseDown e MouseUp:


    ' évt Form1_MouseDown
    Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseDown
        affiche("MouseDown sur formulaire")
    End Sub
 
    ' évt Form1_MouseUp
    Private Sub Form1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseUp
        affiche("MouseUp sur formulaire")
    End Sub

Image

Por fim, o código para o manipulador de eventos Click no botão Delete:


    ' évt btnEffacer_Click
    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' clears the event list
        lstEvts.Items.Clear()
    End Sub

5.6. Criar uma janela com um menu

Agora vamos ver como criar uma janela com um menu. Vamos criar a seguinte janela:

O Controlo 1 é uma caixa de texto de leitura apenas (ReadOnly=true) chamada txtStatut. A árvore do menu é a seguinte:

As opções de menu são controlos tal como outros componentes visuais e possuem propriedades e eventos. Por exemplo, a tabela de propriedades da opção de menu A1:

Image

No nosso exemplo, são utilizadas duas propriedades:

 
o nome do controlo de menu
Texto
o rótulo da opção do menu

As propriedades das várias opções de menu no nosso exemplo são as seguintes:

Nome
Texto
mnuA
opções A
mnuA1
A1
mnuA2
A2
mnuA3
A3
mnuB
opções B
mnuB1
B1
mnuSep1
- (separador)
mnuB2
B2
mnuB3
B3
mnuB31
B31
mnuB32
B32

Para criar um menu, selecione o componente «MainMenu» na barra «ToolBox»:

Image

Terá então um menu vazio colocado no formulário com caixas vazias intituladas «Digite aqui». Basta introduzir as várias opções do menu nessas caixas:

Image

Para inserir um separador entre duas opções, como mostrado acima entre as opções B1 e B2, posicione o cursor no local onde deseja que o separador apareça no menu, clique com o botão direito do rato e selecione a opção Inserir Separador:

Image

Se executar a aplicação utilizando Ctrl+F5, verá um formulário com um menu que ainda não faz nada. As opções do menu são tratadas como componentes: têm propriedades e eventos. Na [janela de código], selecione o componente mnuA1 e, em seguida, selecione os eventos associados:

Image

Se acionar o evento Click acima, o VS.NET gera automaticamente o seguinte procedimento:


Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
    End Sub

Poderíamos proceder desta forma para todas as opções do menu. Aqui, o mesmo procedimento pode ser utilizado para todas as opções. Assim, renomeamos o procedimento anterior *para display* e declaramos os eventos que ele trata:


    Private Sub affiche(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles mnuA1.Click, mnuA2.Click, mnuB1.Click, mnuB2.Click, mnuB31.Click, mnuB32.Click
        ' displays the name of the selected submenu in the TextBox
        txtStatut.Text = (CType(sender, MenuItem)).Text
    End Sub

Neste método, limitamo-nos a apresentar a propriedade Text da opção de menu que desencadeou o evento. O remetente da fonte do evento é do tipo Object. As opções de menu são do tipo MenuItem, pelo que temos de converter de Object para MenuItem aqui. Execute a aplicação e selecione a opção A1 para ver a seguinte mensagem:

Image

O código relevante para esta aplicação, além do método Display, é o código para construir o menu no construtor do formulário (InitializeComponent):


  Private Sub InitializeComponent()
    Me.mainMenu1 = New System.Windows.Forms.MainMenu
    Me.mnuA = New System.Windows.Forms.MenuItem
    Me.mnuA1 = New System.Windows.Forms.MenuItem
    Me.mnuA2 = New System.Windows.Forms.MenuItem
    Me.mnuA3 = New System.Windows.Forms.MenuItem
    Me.mnuB = New System.Windows.Forms.MenuItem
    Me.mnuB1 = New System.Windows.Forms.MenuItem
    Me.mnuB2 = New System.Windows.Forms.MenuItem
    Me.mnuB3 = New System.Windows.Forms.MenuItem
    Me.mnuB31 = New System.Windows.Forms.MenuItem
    Me.mnuB32 = New System.Windows.Forms.MenuItem
    Me.txtStatut = New System.Windows.Forms.TextBox
    Me.mnuSep1 = New System.Windows.Forms.MenuItem
    Me.SuspendLayout()
    ' 
    ' mainMenu1
    ' 
    Me.mainMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA, Me.mnuB})
 
    ' 
    ' mnuA
    ' 
    Me.mnuA.Index = 0
    Me.mnuA.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA1, Me.mnuA2, Me.mnuA3})
    Me.mnuA.Text = "Options A"
    ' 
    ' mnuA1
    ' 
    Me.mnuA1.Index = 0
    Me.mnuA1.Text = "A1"
        ' 
    ' mnuA2
    ' 
    Me.mnuA2.Index = 1
    Me.mnuA2.Text = "A2"
        ' 
    ' mnuA3
    ' 
    Me.mnuA3.Index = 2
    Me.mnuA3.Text = "A3"
        ' 
    ' mnuB
    ' 
    Me.mnuB.Index = 1
    Me.mnuB.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB1, Me.mnuSep1, Me.mnuB2, Me.mnuB3})
 
    Me.mnuB.Text = "Options B"
    ' 
    ' mnuB1
    ' 
    Me.mnuB1.Index = 0
    Me.mnuB1.Text = "B1"
        ' 
    ' mnuB2
    ' 
    Me.mnuB2.Index = 2
    Me.mnuB2.Text = "B2"
 
    ' 
    ' mnuB3
    ' 
    Me.mnuB3.Index = 3
    Me.mnuB3.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB31, Me.mnuB32})
    Me.mnuB3.Text = "B3"
        ' 
    ' mnuB31
    ' 
    Me.mnuB31.Index = 0
    Me.mnuB31.Text = "B31"
        ' 
    ' mnuB32
    ' 
    Me.mnuB32.Index = 1
    Me.mnuB32.Text = "B32"
        ' 
    ' txtStatut
    ' 
    Me.txtStatut.Location = New System.Drawing.Point(8, 8)
    Me.txtStatut.Name = "txtStatut"
    Me.txtStatut.ReadOnly = True
    Me.txtStatut.Size = New System.Drawing.Size(112, 20)
    Me.txtStatut.TabIndex = 0
    Me.txtStatut.Text = ""
    ' 
    ' mnuSep1
    ' 
    Me.mnuSep1.Index = 1
    Me.mnuSep1.Text = "-"
    ' 
    ' Form1
    ' 
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.ClientSize = New System.Drawing.Size(136, 42)
    Me.Controls.Add(txtStatut)
    Me.Menu = Me.mainMenu1
    Me.Name = "Form1"
    Me.Text = "Menus"
    Me.ResumeLayout(False)
  End Sub

Observe a instrução que associa o menu ao formulário:


    Me.Menu = Me.mainMenu1

5.7. Componentes não visuais

Vamos agora analisar vários componentes não visuais: estes são utilizados durante a conceção, mas não são visíveis durante a execução.

5.7.1. Caixas de diálogo OpenFileDialog e SaveFileDialog

Vamos criar a seguinte aplicação:

Os controlos são os seguintes:

N.º
Tipo
nome
função
1
Caixa de texto multilinha
txtText
texto digitado pelo utilizador ou carregado a partir de um ficheiro
2
Botão
btnSave
guarda o texto de 1 num ficheiro de texto
3
Botão
btnLoad
permite-lhe carregar o conteúdo de um ficheiro de texto no 1
4
Botão
btnClear
limpa o conteúdo de 1

São utilizados dois controlos não visuais:

Image

Quando são arrastados da «Caixa de Ferramentas» e soltos no formulário, são colocados numa área separada na parte inferior do formulário. Os componentes «Diálogo» são arrastados da «Caixa de Ferramentas»:

Image

O código para o botão «Limpar» é simples:


    Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnEffacer.Click
        ' clear the input box
        txtTexte.Text = ""
    End Sub

A classe SaveFileDialog é definida da seguinte forma:

Image

Herdou de vários níveis de classes. Das suas muitas propriedades e métodos, iremos concentrar-nos nos seguintes:

string Filter
os tipos de ficheiro apresentados na lista pendente de tipos de ficheiro da caixa de diálogo
int FilterIndex
o índice do tipo de ficheiro apresentado por predefinição na lista acima. Começa em 0.
String
InitialDirectory
a pasta inicialmente apresentada para guardar o ficheiro
string NomeDoFicheiro
O nome do ficheiro de gravação especificado pelo utilizador
DialogResult.ShowDialog()
Um método que apresenta a caixa de diálogo de gravação. Devolve um DialogResult.

O método ShowDialog exibe uma caixa de diálogo semelhante à seguinte:

1
lista suspensa gerada a partir da propriedade Filter. O tipo de ficheiro predefinido é determinado pelo FilterIndex
2
diretório atual, definido por InitialDirectory, caso esta propriedade tenha sido especificada
3
o nome do ficheiro selecionado ou digitado diretamente pelo utilizador. Estará disponível na propriedade FileName
4
Botões Guardar/Cancelar. Se for utilizado o botão Guardar, a função ShowDialog devolve o resultado DialogResult.OK

O procedimento de gravação pode ser escrito da seguinte forma:


    Private Sub btnSauvegarder_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnSauvegarder.Click
        ' save the input box in a text file
        ' set the savefileDialog1 dialog box
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        saveFileDialog1.FilterIndex = 0
        ' display the dialog box and retrieve the result
        If saveFileDialog1.ShowDialog() = DialogResult.OK Then
            ' retrieve the file name
            Dim nomFichier As String = saveFileDialog1.FileName
            Dim fichier As StreamWriter = Nothing
            Try
                ' open the file for writing
                fichier = New StreamWriter(nomFichier)
                ' we write the text inside
                fichier.Write(txtTexte.Text)
            Catch ex As Exception
                ' problem
                MessageBox.Show("Problème à l'écriture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' close the file
                Try
                    fichier.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • Definimos o diretório inicial como o diretório que contém o ficheiro executável da aplicação:
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • Definimos os tipos de ficheiros a apresentar
        saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"

Observe a sintaxe do filtro: filtro1|filtro2|..|filtro, onde filtro = Texto|tipo de ficheiro. Aqui, o utilizador pode escolher entre ficheiros *.txt e *.*.

  • Definimos o tipo de ficheiro a apresentar no início
        saveFileDialog1.FilterIndex = 0

Aqui, os ficheiros do tipo *.txt serão apresentados ao utilizador em primeiro lugar.

  • A caixa de diálogo é apresentada e o seu resultado é recuperado

    If saveFileDialog1.ShowDialog() = DialogResult.OK Then
  • Enquanto a caixa de diálogo estiver visível, o utilizador deixa de ter acesso ao formulário principal (uma chamada caixa de diálogo modal). O utilizador define o nome do ficheiro a guardar e sai da caixa de diálogo clicando no botão Guardar, no botão Cancelar ou fechando a caixa de diálogo. O resultado do método ShowDialog é DialogResult.OK apenas se o utilizador tiver utilizado o botão Guardar para sair da caixa de diálogo.
  • Uma vez feito isto, o nome do ficheiro a ser criado encontra-se agora na propriedade FileName do objeto saveFileDialog1. Regressamos então ao processo padrão de criação de um ficheiro de texto. Escrevemos o conteúdo da TextBox: txtTexte.Text, enquanto tratamos quaisquer exceções que possam ocorrer.

A classe OpenFileDialog é muito semelhante à classe SaveFileDialog e deriva da mesma linhagem de classes. Entre estas propriedades e métodos, iremos concentrar-nos no seguinte:

string Filter
os tipos de ficheiro apresentados na lista pendente de tipos de ficheiro da caixa de diálogo
int FilterIndex
o índice do tipo de ficheiro apresentado por predefinição na lista acima. Começa em 0.
string InitialDirectory
o diretório inicialmente exibido para a pesquisa do ficheiro a abrir
string NomeDoFicheiro
O nome do ficheiro a abrir, conforme especificado pelo utilizador
DialogResult.ShowDialog()
Um método que apresenta a caixa de diálogo de gravação. Devolve um DialogResult.

O método ShowDialog exibe uma caixa de diálogo semelhante à seguinte:

1
lista suspensa criada a partir da propriedade Filter. O tipo de ficheiro predefinido é determinado pelo FilterIndex
2
pasta atual, definida por InitialDirectory, caso esta propriedade tenha sido especificada
3
Nome do ficheiro selecionado ou digitado diretamente pelo utilizador. Estará disponível na propriedade FileName
4
Botões Abrir/Cancelar. Se o botão Abrir for utilizado, a função ShowDialog devolve o resultado DialogResult.OK

O procedimento aberto pode ser escrito da seguinte forma:


Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCharger.Click
        ' load a text file into the input box
        ' set the openfileDialog1 dialog box
        openFileDialog1.InitialDirectory = Application.ExecutablePath
        openFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
        openFileDialog1.FilterIndex = 0
        ' display the dialog box and retrieve the result
        If openFileDialog1.ShowDialog() = DialogResult.OK Then
            ' retrieve the file name
            Dim nomFichier As String = openFileDialog1.FileName
            Dim fichier As StreamReader = Nothing
            Try
                ' open the file in read mode
                fichier = New StreamReader(nomFichier)
                ' read the entire file and put it in the TextBox
                txtTexte.Text = fichier.ReadToEnd()
            Catch ex As Exception
                ' problem
                MessageBox.Show("Problème à la lecture du fichier (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' close the file
                Try
                    fichier.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • Definimos o diretório inicial como o diretório que contém o executável da aplicação:
            saveFileDialog1.InitialDirectory=Application.ExecutablePath
  • Definimos os tipos de ficheiros a apresentar
            saveFileDialog1.Filter = "Fichiers texte (*.txt)|*.txt|Tous les fichiers (*.*)|*.*"
  • Definir o tipo de ficheiro a apresentar em primeiro lugar
            saveFileDialog1.FilterIndex = 0

Aqui, os ficheiros *.txt serão apresentados ao utilizador em primeiro lugar.

  • A caixa de diálogo é apresentada e o seu resultado é recuperado

    If openFileDialog1.ShowDialog() = DialogResult.OK Then

Enquanto a caixa de diálogo estiver visível, o utilizador deixa de ter acesso ao formulário principal (uma chamada caixa de diálogo modal). O utilizador especifica o nome do ficheiro a abrir e sai da caixa de diálogo clicando no botão Abrir, no botão Cancelar ou fechando a caixa de diálogo. O resultado do método ShowDialog é DialogResult.OK apenas se o utilizador tiver utilizado o botão Abrir para sair da caixa de diálogo.

  • Feito isto, o nome do ficheiro a ser criado encontra-se agora na propriedade FileName do objeto openFileDialog1. Passamos então ao processo padrão de leitura de um ficheiro de texto. Repare no método que permite ler o ficheiro na íntegra:
                    txtTexte.Text=fichier.ReadToEnd
  • O conteúdo do ficheiro é colocado na caixa de texto txtTexte. Tratamos quaisquer exceções que possam ocorrer.

5.7.2. Caixas de diálogo FontColor e ColorDialog

Damos continuidade ao exemplo anterior, introduzindo dois novos botões:

N.º
tipo
nome
função
6
Botão
btnColor
para definir a cor do texto da caixa de texto
7
Botão
btnFont
para definir o tipo de letra da caixa de texto

Colocamos um controlo ColorDialog e um controlo FontDialog no formulário:

Image

As classes FontDialog e ColorDialog têm um método ShowDialog semelhante ao método ShowDialog das classes OpenFileDialog e SaveFileDialog. O método ShowDialog da classe ColorDialog permite-lhe escolher uma cor:

Image

Se o utilizador fechar a caixa de diálogo utilizando o botão OK, o resultado do método ShowDialog será DialogResult.OK, e a cor selecionada será guardada na propriedade Color do objeto ColorDialog utilizado. O método ShowDialog da classe FontDialog permite selecionar um tipo de letra:

Image

Se o utilizador fechar a caixa de diálogo clicando no botão OK, o resultado do método ShowDialog é DialogResult.OK, e o tipo de letra selecionado é armazenado na propriedade Font do objeto FontDialog utilizado. Temos os elementos para tratar os cliques nos botões Color e Font:


    Private Sub btnCouleur_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCouleur.Click
        ' choice of text color
        If colorDialog1.ShowDialog() = DialogResult.OK Then
            ' change the forecolor property of TextBox
            txtTexte.ForeColor = colorDialog1.Color
        End If
    End Sub
 
 
    Private Sub btnPolice_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnPolice.Click
        ' font selection
        If fontDialog1.ShowDialog() = DialogResult.OK Then
            ' change the font property of TextBox
            txtTexte.Font = fontDialog1.Font
        End If
    End Sub

5.7.3. Temporizador

Aqui, propomos escrever a seguinte aplicação:

N.º
Tipo
Nome
Função
1
Caixa de texto, ReadOnly=true
txtChrono
exibe um temporizador
2
Botão
btnStopStart
Botão de parar/iniciar para o cronómetro
3
Temporizador
timer1
componente que aciona um evento a cada segundo

O temporizador está a funcionar:

Image

O cronómetro parou:

Image

Para atualizar o conteúdo da caixa de texto txtChrono a cada segundo, precisamos de um componente que gere um evento a cada segundo, que possamos interceptar para atualizar a exibição do cronómetro. Esse componente é o Timer:

Image

Assim que este componente é adicionado ao formulário (na secção de componentes não visuais), é criado um objeto Timer no construtor do formulário. A classe System.Windows.Forms.Timer é definida da seguinte forma:

Image

Das suas propriedades, iremos considerar apenas as seguintes:

Intervalo
número de milissegundos após o qual um evento Tick é acionado.
Tick
o evento acionado ao final do intervalo de milissegundos
Ativado
define o temporizador como ativo (true) ou inativo (false)

No nosso exemplo, o temporizador chama-se timer1 e timer1.Interval está definido para 1000 ms (1 s). O evento Tick ocorrerá, portanto, a cada segundo. Um clique no botão Iniciar/Parar é tratado pelo seguinte procedimento:


    Private Sub btnArretMarche_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnArretMarche.Click
        ' off or on?
        If btnArretMarche.Text = "Marche" Then
            ' we note the start time
            début = DateTime.Now
            ' we display it
            txtChrono.Text = "00:00:00"
            ' start timer
            timer1.Enabled = True
            ' change the button label
            btnArretMarche.Text = "Arrêt"
            ' end
            Return
        End If        '
        If btnArretMarche.Text = "Arrêt" Then
            ' timer off
            timer1.Enabled = False
            ' change the button label
            btnArretMarche.Text = "Marche"
            ' end
            Return
        End If
    End Sub
 
    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' a second has passed
        Dim maintenant As DateTime = DateTime.Now
        Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
        txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
    End Sub

A etiqueta do botão Iniciar/Parar é "Parar" ou "Iniciar". Por isso, temos de verificar esta etiqueta para determinar o que fazer.

  • Se o rótulo for "Start", armazenamos a hora de início numa variável global do objeto de formulário, o temporizador é iniciado (Enabled=true) e o rótulo do botão muda para "Stop".
  • Se o rótulo for «Stop», o temporizador é parado (Enabled=false) e o rótulo do botão muda para «Start».

Public Class Timer1
  Inherits System.Windows.Forms.Form
  Private WithEvents timer1 As System.Windows.Forms.Timer
  Private WithEvents btnArretMarche As System.Windows.Forms.Button
  Private components As System.ComponentModel.IContainer
  Private WithEvents txtChrono As System.Windows.Forms.TextBox
  Private WithEvents label1 As System.Windows.Forms.Label
 
  ' instance variables
  Private début As DateTime

O atributo start acima é reconhecido em todos os métodos da classe. Ainda precisamos de tratar o evento Tick no objeto timer1, um evento que ocorre a cada segundo:


    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' a second has passed
        Dim maintenant As DateTime = DateTime.Now
        Dim durée As TimeSpan = DateTime.op_Subtraction(maintenant, début)
        txtChrono.Text = "" + durée.Hours.ToString("d2") + ":" + durée.Minutes.ToString("d2") + ":" + durée.Seconds.ToString("d2")
    End Sub

Calculamos o tempo decorrido desde que o cronómetro foi iniciado. Obtemos um objeto TimeSpan que representa uma duração. Este deve ser apresentado no cronómetro no formato hh:mm:ss. Para tal, utilizamos as propriedades Hours, Minutes e Seconds do objeto TimeSpan, que representam as horas, os minutos e os segundos da duração, respetivamente. Apresentamo-las utilizando o formato ToString("d2") para garantir uma exibição com dois dígitos.

5.8. O exemplo de cálculo de impostos

Voltamos à aplicação IMPOTS, que já abordámos duas vezes. Agora, adicionamos-lhe uma interface gráfica de utilizador:

Os controlos são os seguintes

N.º
tipo
nome
função
1
Botão de opção
rdYes
marcado se for casado
2
Botão de opção
rdNão
Marcado se não for casado
3
Número para cima/para baixo
incChildren
número de filhos do contribuinte
Mínimo=0, Máximo=20, Incremento=1
4
Caixa de Texto
txtSalário
salário anual do contribuinte em F
5
Caixa de Texto
txtImpostos
montante do imposto devido
ReadOnly=true
6
Botão
btnCalcular
inicia o cálculo do imposto
7
Botão
btnLimpar
redefine o formulário para o seu estado inicial ao carregar
8
Botão
btnExit
para sair da aplicação

Regras de funcionamento

  • O botão «Calcular» permanece desativado enquanto o campo do salário estiver vazio
  • Se, ao executar o cálculo, o salário estiver incorreto, é apresentada uma mensagem de erro:

Image

O programa é apresentado abaixo. Utiliza a classe Impot criada no capítulo sobre classes. Parte do código gerado automaticamente pelo VS.NET não foi reproduzido aqui.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' options
Option Explicit On 
Option Strict On
 
' namespaces
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
 
' form class
Public Class frmImpots
  Inherits System.Windows.Forms.Form
  Private WithEvents label1 As System.Windows.Forms.Label
  Private WithEvents rdOui As System.Windows.Forms.RadioButton
  Private WithEvents rdNon As System.Windows.Forms.RadioButton
  Private WithEvents label2 As System.Windows.Forms.Label
  Private WithEvents txtSalaire As System.Windows.Forms.TextBox
  Private WithEvents label3 As System.Windows.Forms.Label
  Private WithEvents label4 As System.Windows.Forms.Label
  Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
  Private WithEvents btnCalculer As System.Windows.Forms.Button
  Private WithEvents btnEffacer As System.Windows.Forms.Button
  Private WithEvents btnQuitter As System.Windows.Forms.Button
  Private WithEvents txtImpots As System.Windows.Forms.TextBox
  Private components As System.ComponentModel.Container = Nothing
  Private WithEvents incEnfants As System.Windows.Forms.NumericUpDown
 
  ' data tables required for tax calculation
  Private limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
  Private coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.55D, 0.5D, 0.6D, 0.65D}
  Private coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}
  ' tax object
  Private objImpôt As impot = Nothing
 
 
  Public Sub New()
    InitializeComponent()
    ' form initialization
    btnEffacer_Click(Nothing, Nothing)
    btnCalculer.Enabled = False
    ' tax object creation
    Try
      objImpôt = New impot(limites, coeffR, coeffN)
    Catch ex As Exception
      MessageBox.Show("Impossible de créer l'objet impôt (" + ex.Message + ")", "Erreur", MessageBoxButtons.OK, MessageBoxIcon.Error)
      ' inhibit the salary entry field
      txtSalaire.Enabled = False
    End Try 'try-catch
  End Sub 

  Protected Overloads Sub Dispose(ByVal disposing As Boolean)
....
  End Sub

  Private Sub InitializeComponent()
    Me.btnQuitter = New System.Windows.Forms.Button
    Me.groupBox1 = New System.Windows.Forms.GroupBox
    Me.btnEffacer = New System.Windows.Forms.Button
    Me.btnCalculer = New System.Windows.Forms.Button
    Me.txtSalaire = New System.Windows.Forms.TextBox
    Me.label1 = New System.Windows.Forms.Label
    Me.label2 = New System.Windows.Forms.Label
    Me.label3 = New System.Windows.Forms.Label
    Me.rdNon = New System.Windows.Forms.RadioButton
    Me.txtImpots = New System.Windows.Forms.TextBox
    Me.label4 = New System.Windows.Forms.Label
    Me.rdOui = New System.Windows.Forms.RadioButton
    Me.incEnfants = New System.Windows.Forms.NumericUpDown
    Me.groupBox1.SuspendLayout()
    CType(Me.incEnfants, System.ComponentModel.ISupportInitialize).BeginInit()
    Me.SuspendLayout()
....
  End Sub 'InitializeComponent
 
  Public Shared Sub Main()
    Application.Run(New frmImpots)
  End Sub 'Main
 
 
  Private Sub btnEffacer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnEffacer.Click
    ' raz du formulaire
    incEnfants.Value = 0
    txtSalaire.Text = ""
    txtImpots.Text = ""
    rdNon.Checked = True
  End Sub 'btnEffacer_Click
 
 
  Private Sub txtSalaire_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles txtSalaire.TextChanged
    ' calculate button status
    btnCalculer.Enabled = txtSalaire.Text.Trim() <> ""
  End Sub 'txtSalaire_TextChanged
 
 
  Private Sub btnQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnQuitter.Click
    ' end application
    Application.Exit()
  End Sub 'btnQuitter_Click
 

  Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnCalculer.Click
    ' is the salary correct?
    Dim intSalaire As Integer = 0
    Try
      ' salary recovery
      intSalaire = Integer.Parse(txtSalaire.Text)
      ' it must be >=0
      If intSalaire < 0 Then
        Throw New Exception("")
      End If
    Catch ex As Exception
      ' error msg
      MessageBox.Show(Me, "Salaire incorrect", "Erreur de saisie", MessageBoxButtons.OK, MessageBoxIcon.Error)
      ' focus on wrong field
      txtSalaire.Focus()
      ' select text for input field
      txtSalaire.SelectAll()
      ' back to visual interface
      Return
    End Try 'try-catch
    ' salary is correct - tax is calculated
    txtImpots.Text = "" & CLng(objImpôt.calculer(rdOui.Checked, CInt(incEnfants.Value), intSalaire))
  End Sub 'btnCalculer_Click
End Class

Aqui utilizamos o assembly impots.dll, que é o resultado da compilação da classe impots do Capítulo 2. Recorde-se que este assembly pode ser gerado no modo de consola utilizando o comando

dos>vbc /t:library impots.vb

Este comando gera o ficheiro impots.dll, conhecido como assembly. Este assembly pode então ser utilizado em vários projetos. Aqui, no nosso projeto no VS.NET, utilizamos a janela de propriedades do projeto:

Image

Para adicionar uma referência (um assembly), clicamos com o botão direito do rato no grupo Referências acima, selecionamos a opção [Adicionar Referência] e especificamos o assembly [impots.dll] que colocámos na pasta do projeto:

dos>dir
01/03/2004  14:39                9 250 gui_impots.vb
01/03/2004  14:37                4 096 impots.dll
01/03/2004  14:41               12 288 gui_impots.exe

Assim que o assembly [impots.dll] for incluído no projeto, a classe [impots] passa a ser reconhecida pelo projeto. Anteriormente, isso não acontecia. Outro método consiste em incluir o ficheiro fonte impots.vb no projeto. Para tal, na janela de propriedades do projeto, clique com o botão direito do rato no projeto, selecione a opção [Adicionar/Adicionar Item Existente] e especifique o ficheiro impots.vb.