O namespace System.Collections.Generic
contém um conjunto de interfaces e classes que permitem criar colecções muito seguras,
simples de utilizar e com muitos bons performances. Estas colecções de Generics
são do tipo strongly typed, ou seja,
estão disponíveis no intelissense e verificadas
pelo compilador, o que evita erros e torna mais fácil a vida ao programador.
Existem diversos tipos de colecções
neste ou em outros namespaces (como por exemplo o Dictionary, SortedList, ArrayList,
etc) mas um muito utilizado é o List(Of (T)), onde se define o tipo da colecção
que se pretende.
Um exemplo simples de utilização:
' Cria uma nova lista
do tipo "string"
Dim
lstEmpreg As New
List(Of String)
' Adiciona alguns nomes
à lista
lstEmpreg.Add("João Paulo")
lstEmpreg.Add("Pedro
Sousa")
lstEmpreg.Add("Luís Nascimento")
lstEmpreg.Add("Carlos
Sousa")
lstEmpreg.Add("Nuno Luís")
Dim nomePesquisar
As String = "Pedro Sousa"
' Caso o nome a pesquisar
seja encontrado
If lstEmpreg.Contains(nomePesquisar)
Then
' Mostra
o nome e o total de nomes na lista
Dim
msg As String
= "O nome {0} foi encontrado em {1} nome(s)"
Debug.WriteLine(String.Format(msg,
nomePesquisar, lstEmpreg.Count))
Else
' Mensagem
caso não encontre o nome na lista
Debug.WriteLine("Nome
não encontrado na lista!")
End
If
Mas esta colecção
pode ser muito mais complexa, sendo necessário guardar vários dados (para além do
nome), como por exemplo o número, idade, telefone, etc. Neste caso será criada uma
estrutura e guardada na lista.
O método de pesquisa
implementa um IEquatable
e utiliza um Predicate.
Desta forma não é necessário percorrer todos os items da lista para verificar qual
o item correspondente.
Um exemplo de utilização
Construção de uma
classe simples, sem utilização de propriedades
(get/set), que implementa o IEquatable (apenas para demonstração):
Exemplo:
'
Construção de uma classe simples, sem utilização de
‘
propriedades (get/set) que implementa o IEquatable
Public
Class Empregado
Implements
IEquatable(Of Empregado)
'
Campos da classe
Public ID
As Byte
Public Nome
As String
Public Idade
As Byte
Public
Function ProcuraEmpregado(ByVal EmpID
As Empregado) _
As
Boolean Implements
System.IEquatable(Of Empregado).Equals
' Retorna o ID mas poderá ser utilizado outro campo
' para pesquisa, sendo
apenas necessário altera para
' o campo correctos (ex.
Me.Nome = EmpID.Nome)
Return Me.ID
= EmpID.ID
End
Function
End
Class
Utilização da List(Of
T) que neste caso adiciona alguns empregados e pesquisa por um campo mostrando os
resultados. O campo escolhido para o exemplo foi o ID mas pode-se pesquisar por
qualquer campo.
' Cria uma nova lista
do tipo "Empregado"
Dim lstEmpregados
As New List(Of Empregado)
' Adiciona empregados à lista
Dim Emp1 As New Empregado
Emp1.ID = 1
Emp1.Nome = "João Paulo"
Emp1.Idade = 28
lstEmpregados.Add(Emp1)
Dim Emp2 As New Empregado
Emp2.ID = 2
Emp2.Nome = "Pedro Sousa"
Emp2.Idade = 25
lstEmpregados.Add(Emp2)
Dim Emp3 As New Empregado
Emp3.ID = 3
Emp3.Nome = "Luís Nascimento"
Emp3.Idade = 41
lstEmpregados.Add(Emp3)
' Empregado a procurar
na lista
Dim Emp As New Empregado
Emp.ID = 2
' Guarda o resultado da
procura na variável EmpResult
Dim EmpResult
As Empregado
' Pesquisa na lista de
empregados usando um Predicate do tipo empregado
EmpResult = lstEmpregados.Find(New
System.Predicate(Of Empregado)(AddressOf
Emp.ProcuraEmpregado))
' Caso tenha encontrado
mostra os resultados ou a mensagem de erro
If
EmpResult IsNot Nothing
Then
Debug.WriteLine("Número: "
& EmpResult.ID.ToString)
Debug.WriteLine("Empregado: " & EmpResult.Nome.ToString)
Debug.WriteLine("Idade:
" & EmpResult.Idade.ToString)
Else
Debug.WriteLine("Número
de empregado não encontrado")
End
If
Resultado:
Número: 2
Empregado: Pedro Sousa
Idade: 25
As colecções foram completamente revolucionadas
com a versão .NET e a sua utilização vem melhorar o que, no passado com o VB6, se
fazia apenas com as Collections (muito limitadas) ou com a utilização de arrays.
São bastante simples de utilizar e
é recomendável que se verifique qual a colecção que mais se adequa às necessidades
no programa.
PS: Como sempre, qualquer dúvida,
comentário ou correcção ao artigo é sempre bem vinda!
Unhandled Exceptions
Uma boa parte do código é dedicada (ou deveria ser) para tratamento de erros, previstos ou imprevistos, de modo a mostrar ao utilizador final uma mensagem adequada a todo o tipo de situações. O normal é mostrar uma mensagem de erro mas há também quem a registe para análise, envie via e-mail, etc, etc.
No entanto existem sempre zonas do código “esquecidas” e erros que não estão previstos, por muito cuidado que se tenha e por muitas situações que se contemple.
É sempre incómodo e pouco profissional mostrar ao utilizador uma mensagem não personalizada quando ocorre um erro ou que, a aplicação encerre inexplicavelmente.
Para simplificar a vida ao programador, o Visual Studio.NET dispõe de eventos a nível da aplicação (e não específicos para um único form ou objecto) que incluem os eventos Startup, Shutdown, StartupNextInstance, NetworkAvailabilityChanged e UnhandledException (excepções não controladas). Desta forma consegue-se controlar facilmente alguns eventos gerais.
Neste exemplo será ainda mostrado como aceder a estes eventos (Visual Basic Application Model), apenas o UnhandledException, e registar no Event Log do Windows a mensagem gerada. Para implementar este exemplo seguir os seguintes passos:
1 – Menu Poject – Properties – e na Application Tab clicar no botão View Application Events;
2 – Na dropdownlist das classes seleccionar MyApplication Events
3 – Na dropdownlist dos métodos seleccionar UnhandledException
Isto irá originar um novo Sub chamado "MyApplication_UnhandledException" e também alguns comentários com informação sobre os métodos disponíveis. Finalmente utilizar o seguinte código:
Namespace My
Partial Friend Class MyApplication
Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs)
Handles Me.UnhandledException
' Verifica o nome da aplicação
Dim AppName As String = My.Application.Info.Title
' Define o nome do grupo a criar no event log
Dim LogName As String = AppName & "LOG"
' Cria um nova instância do EventLog
Dim log As New EventLog()
' Caso o grupo não exista, cria um novo
If Not EventLog.SourceExists(AppName) Then
EventLog.CreateEventSource(AppName, LogName)
End If
' Define o nome da fonte do log
log.Source = AppName
' Define a mensagem e escreve no ficheiro
Dim msg As String = "Ocorreu uma erro no programa." & vbCrLf & _
vbCrLf & e.Exception.Message
log.WriteEntry(msg, System.Diagnostics.EventLogEntryType.Error)
log.Dispose()
' Mosta a mensagem ao utilizador
MessageBox.Show(msg)
' Define que a aplicação não termina
e.ExitApplication = False
End Sub
End Class
End Namespace
Finalmente para testar é só executar algo do género, associado a um botão ou outro evento:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Byte = 50
Dim b As Byte = 50
Dim c As Byte = a * b
End Sub
Como o Byte suporta valores numéricos de 0 a 255, e o resultado desta operação é 2500, irá originar um excepção ou erro.
NOTA: Como o Visual Studio mostra o erro, seleccionando a linha, não é possível testar este código em modo de debug (teste) mas apenas em runtime (execução).
Passar informação entre Forms
Uma dúvida frequente é a forma como passar informação entre forms. Existem diversas formas de o fazer, umas mais correctas do que outras, devido a inúmeros factores. No entanto algumas são mais “recomendadas” porque não utilizam variáveis globais/públicas em módulos ou classes. Ficam dois exemplos de como se pode fazer:
#Método 1 – Enviar / Receber
Primeiro Form (master) que irá passar uma variável e receber outra através de uma nova propriedade criada:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
' Criação de uma nova instância
Dim frm As New frmChild
' Atribuição de um valor à propriedade
frm.myString = "vbtuga - teste"
' Mostra o frm como dialog, ou seja o resto do código
' só irá executar-se quando este for fechado
frm.ShowDialog()
' Mostra a nova mensagem
MessageBox.Show(frm.myString)
End Sub
Segundo Form (child) onde é criada uma propriedade e retornado um novo valor
' Variável auxiliar da propriedade
Private _myString As String
' Criação de uma propriedade que pode passar informação
' em ambos os sentidos (master->child, child->master)
Public Property myString() As String
Set(ByVal value As String)
_myString = value
End Set
Get
Return _myString
End Get
End Property
Private Sub frmChild_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Exibição da mensagem
MessageBox.Show(Me.myString)
' Definição de novo texto da propriedade
Me.myString = "Nova string"
End Sub
#Método 2 – Enviar
Primeiro Form (master) que irá passar uma variável:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
' Criação de uma nova instância e define o texto como parâmetro
Dim frm As New frmChild("vbtuga - teste")
frm.Show()
End Sub
Segundo Form (child) onde é alterado o construtor e exibida a mensagem
Private myString As String
' Definição de um parâmetro no construtor do frm
Sub New(ByVal str As String)
InitializeComponent()
' Actribuição do valor passado à variável privada
myString = str
End Sub
Private Sub frmChild_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Exibição da mensagem
MessageBox.Show(myString)
End Sub
Dois exemplos simples mas muito úteis, porque este intercambio de informação entre forms é muito usual.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
Um dos factores que determinou o grande
sucesso do Microsoft Excel, para além da sua grande capacidade como folha de cálculo,
foi sem dúvida a grande diversidade de representação gráfica. Os gráficos permitem
mostrar de uma forma simples e visualmente muito agradável os resultados obtidos.
Existem dezenas de gráficos, com diferentes tipos e formatos que permitem agradar
a todos, exigindo apenas algum trabalho e bom gosto.
Mas no entanto, existem sempre coisas
que se necessita personalizar, quer seja os formatos, os textos, as cores, etc.
Muitas vezes é necessário (ou importante) alterar o gráfico de acordo com os resultados
e se automatizarmos esta personalização de acordo com um determinado critério, além
de simplificar o trabalho, evita também erros indesejados.
Uma das representações em gráfico
muito utilizadas é a definição de um valor e o objectivo a atingir. O seguinte exemplo
mostra como alterar a cor das barras de um gráfico de acordo com um objectivo. Neste
exemplo as barras ficam a verdes caso o resultado seja igual ou superior a um objectivo
definido (objectivo cumprido) e a vermelho caso estejam abaixo do objectivo.
Os gráficos funcionam com séries,
e um gráfico pode ter várias séries com diferentes tipos (barras, linhas, etc).
Cada série tem um conjunto de pontos que correspondem aos valores que são representados.
(figura 1)
Para implementarmos este exemplo é
apenas necessário criar uma tabela com alguns valores para o resultado (vendas)
e definir um objectivo (figura 1).
Depois abrir o Editor de Visual Basic
(Tools – Macros – Visual Basic Editor ou ALT+F11) e definir para a Sheet1 o seguinte
código:
' Código para a "Sheet1"
que executa caso algum valor seja alterado
Private Sub Worksheet_Change(ByVal
Target As Range)
' Caso seja alterado
algum valor na área B20:H21
If Not
Intersect(Target, [B20:H21]) Is Nothing Then
Application.ScreenUpdating = False
' Definição de variáveis
Dim ws As Worksheet
Dim
wsChartObject As ChartObject
Dim
wsCell As Range
' Atribuição de objectos às variáveis indicando qual é a worksheet
e
' qual é o nome do gráfico (visível na folha de cálculo)
Set ws = Worksheets("Sheet1")
Set
wsChartObject = ws.ChartObjects("Chart 1")
' Inicia um ciclo em todas as células da primeira linha (resultados
de vendas)
For Each wsCell In
ws.[B20:H20].Cells
' Selecciona a série 1 no ponto de acordo com
a coluna da célula actual
' pode-se alterar a coluna (column) para linha
(row) caso os dados estejam
' na posição vertical.
With wsChartObject.Chart.SeriesCollection(1).Points(wsCell.Column - 1).Interior
' Caso o valor da célula actual
seja igual ou superior à célula que está
' na linha abaixo da actual, coloca
diferentes cores. O valor desta célula
' na linha abaixo (objectivo) é
adquirida com a implementando a função
' OffSet que permite indicar uma
posição a partir da posição actual
If wsCell >=
wsCell.Offset(1) Then
.ColorIndex = 10 ' Verde
Else
.ColorIndex = 3
' Vermelho
End If
End With
Next
' Limpa as variáveis da memória
Set
ws = Nothing
Set
wsChartObject = Nothing
Application.ScreenUpdating
= True
End If
End Sub
O resultado final
(figura 2) pode ser visto alterando qualquer valor no Range definido no evento
Worksheet Change (que no exemplo é B20:H20)
(figura 2)
Este exemplo pretende mostrar como
modificar dinamicamente as cores de um gráfico com base num determinado critério,
mostrando de uma forma geral que é possível utilizar o VBA para efectuar modificações
nos gráficos de uma forma relativamente simples.
PS: Como sempre, qualquer dúvida,
comentário ou correcção ao artigo é sempre bem vinda!
A utilização e manipulação de ficheiros de texto (*. txt ou outra extensão) é muito comum embora existam bastantes alternativas disponíveis para guardar informação. No entanto, muitas vezes não é uma questão de alternativa (usar ou não usar) mas sim uma necessidade.
O .NET Framework 2.0 introduziu novos métodos que possibilitam um fácil e rápido acesso. Os métodos mais utilizados e mais eficazes para ler e escrever em ficheiros de texto são os namespaces IO (através da classe File, principalmente ReadAllLines, WriteAllText e AppendAllText e o StreamReader, StreamWriter) e o My (através da classe My.Computer.FileSystem, principalmente ReadAllText e WriteAllText).
Obviamente que existem diferenças entre eles, mas as principais diferenças são que o StreamReader e StreamWriter lê/escreve linha a linha e os outros métodos lêem/escrevem tudo de uma só vez, ou seja, utilizam mais recursos da aplicação. Por exemplo o StreamReader lê linha a linha enquanto o método ReadAllLines lê toda a informação para uma variável (array).
É claro que isto apenas se consegue notar em grande número de informação, mas para verem as principais diferenças e desempenhos vejam a seguinte tabela com os tempos de acesso em milisegundos.
Tempo Médio de Leitura
| Método/Nº Linhas | 10.000 | 100.000 | 1.000.000 |
| IO.File.ReadAllLines | 4,0 | 71,2 | 957,1 |
| IO.StreamReader | 2,6 | 33,9 | 313,1 |
| My.Computer.FileSystem.ReadAllText | 6,7 | 60,5 | 991,2 |
Tempo Médio de Leitura
| Método/Nº Linhas | 10.000 | 100.000 | 1.000.000 |
| IO.File.WriteAllText | 15,1 | 123,8 | 1.426,4 |
| IO.StreamWriter | 13,8 | 115,5 | 1.922,8 |
| My.Computer.FileSystem.WriteAllText | 15,3 | 116,8 | 1.389,1 |
Nota: Tempo em milisegundos com a média de 10 leitura e texto por linha de 13 caracteres
Para mostrar um pouco a utilização dos diferentes métodos serão mostrados dois exemplos da leitura e escrita. Como o IO.File e o My.Computer.FileSystem são bastante semelhantes, onde a principal diferença é que a classe IO.File tem um método que lê todas as linhas sem necessitar de utilizar uma função de Split() para as colocar num array, irá ser mostrado apenas este. O My.Computer.FileSystem tem ainda um parametro adicionar que permite adicionar, ou não, texto a um arquivo já existente (append).
Exemplo de leitura de um ficheiro através do classe IO.File (ReadAllLines)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Guarda num Array linha a linha
Dim lines() As String = IO.File.ReadAllLines(fileName)
' Ciclo que irá mostrar linha a linha
For Each line As String In lines
Debug.WriteLine(line.ToString)
total += 1
Next
' Mostra o total de linha lidas
Debug.WriteLine("Total de linhas:" & total.ToString)
Exemplo de escrita de um ficheiro através do classe IO.File (WriteAllText)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Cria um novo StringBuilder
Dim sb As New System.Text.StringBuilder
' Ciclo que irá inserir construir o ficheiro a gravar
For x As Integer = 0 To 9999
' Cria uma linha com o formato "Linha nº 0001"
sb.AppendLine("Linha nº " & x.ToString.PadLeft(4, "0"))
total += 1
Next
' Grava o ficheiro no disco
IO.File.WriteAllText(fileName, sb.ToString)
' Mostra o resultado
Debug.WriteLine(String.Format("Ficheiro {0} com {1} registos", fileName, total))
Exemplo de leitura de um ficheiro através do classe IO (StreamReader)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Ciclo que irá mostrar linha a linha
Using reader As New IO.StreamReader(fileName)
' Ciclo até ao fim do ficheiro
While Not reader.EndOfStream()
' Mostra o resultado linha a linha
Dim input As String = reader.ReadLine()
Debug.WriteLine(input)
total += 1
End While
End Using
' Mostra o total de linha lidas
Debug.WriteLine("Total de linhas:" & total.ToString)
Exemplo de escrita de um ficheiro através do classe IO (StreamWriter)
Dim total As Integer = 0
Dim fileName As String = "c:\teste.txt"
' Cria um nova instância de um StreamWriter
Using writer As New IO.StreamWriter(fileName)
' Inicia o ciclo que irá gravar no ficheiro
For x As Integer = 0 To 9999
writer.WriteLine("Linha nº " & x.ToString.PadLeft(4, "0"))
total += 1
Next
End Using
' Mostra o resultado
Debug.WriteLine(String.Format("Ficheiro {0} com {1} registos", fileName, total))
Estes são pequenos exemplos da estrutura tipo, embora existam ainda em cada classe, uma serie de métodos que podem ser utilizados. Pelos exemplos mostrados e pelo desempenho de cada um, deverá ser utilizado aquele que mais se ajustar a cada situação. Apenas em ficheiros grandes deverá haver um mais cuidado na escolha do método a utilizar.
Todos utilizam por defeito uma codificação de UTF-8 mas por vezes é necessário ajustar para certos ficheiros. A opção System.Text.Encoding.Default é muitas vezes a melhor solução para problemas de codificação.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!