O Microsoft Excel permite colocar comentários nas células, de modo a guardar informações não visíveis(escondidas), mas que quando o ponteiro do rato está sobre a respectiva célula, o comentário é exibido sobre a forma de Tooltip.
Este comentário é representado por um pequeno triângulo, de cor vermelha, ao canto superior direito da célula.
A DataGridView é um controlo muito semelhante às células do Microsoft Excel, mas como é natural, esta possibilidade não está contemplada. A DataGridView não é uma folha de calculo!
No entanto, poderá ser útil, reproduzir esta funcionalidade em uma DataGridView, para guardar alguns comentários, notas pessoais, etc.

Para implementarmos este tipo de funcionalidade, podemos criar um novo tipo de coluna – DataGridViewColumnComment - e um novo tipo de célula -DataGridViewCellComment. Depois, no evento Paint(), criamos o triângulo ao canto superior direito da respectiva célula.
O resultado, será uma classe como esta:
''' <summary>
''' Cria um novo tipo de coluna
''' </summary>
<System.Diagnostics.DebuggerStepThrough()> _
Public Class DataGridViewColumnComment
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New DataGridViewCellComment())
End Sub
End Class
''' <summary>
''' Cria uma nova TextBoxCell que irá permitir
''' adicionar comentários e mostrar como ToolTipText
''' </summary>
<System.Diagnostics.DebuggerStepThrough()> _
Public Class DataGridViewCellComment
Inherits DataGridViewTextBoxCell
Private m_Comment As String
''' <summary>
''' Guarda a informação dos comentários
''' </summary>
Public Property Comment() As String
Get
Return m_Comment
End Get
Set(ByVal value As String)
m_Comment = value
' Define o texto para a tooltip
If m_Comment IsNot Nothing AndAlso m_Comment.Length <> 0 Then
Me.ToolTipText = m_Comment
Else
Me.ToolTipText = String.Empty
End If
End Set
End Property
Protected Overrides Sub Paint(ByVal graphics As System.Drawing.Graphics, _
ByVal clipBounds As System.Drawing.Rectangle, _
ByVal cellBounds As System.Drawing.Rectangle, _
ByVal rowIndex As Integer, _
ByVal cellState As System.Windows.Forms.DataGridViewElementStates, _
ByVal value As Object, _
ByVal formattedValue As Object, _
ByVal errorText As String, _
ByVal cellStyle As System.Windows.Forms.DataGridViewCellStyle, _
ByVal advancedBorderStyle As _
System.Windows.Forms.DataGridViewAdvancedBorderStyle, _
ByVal paintParts As System.Windows.Forms.DataGridViewPaintParts)
' Desenha a célula (processo normal)
MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, _
cellState, value, formattedValue, errorText, _
cellStyle, advancedBorderStyle, paintParts)
' Caso tenha algum comentário, desenha o triângulo
If Me.Comment IsNot Nothing AndAlso Me.Comment.Length <> 0 Then
Dim rect As Rectangle = cellBounds
Dim points As New List(Of Point)
points.Add(New Point(rect.Right - 8, rect.Top))
points.Add(New Point(rect.Right, rect.Top + 8))
points.Add(New Point(rect.Right, rect.Top))
graphics.FillPolygon(Brushes.Red, points.ToArray)
End If
End Sub
End Class
Depois, para adicionar colunas à DataGridView:
Dim col As New DataGridViewColumnComment
col.Name = "Nova Coluna"
col.HeaderText = "Nova Coluna"
Me.DataGridView1.Columns.Add(col)
Finalmente, para inserir ou remover comentários, é apenas necessário altera a propriedade Comment. Colocando qualquer String, o comentário é definido, caso contrário, é removido.
' Posição na DataGridView
Dim row As Integer = 0
Dim col As Integer = 0
' Converte a célula para o tipo criado
Dim cell As DataGridViewCellComment = _
DirectCast(Me.DataGridView1(col, row), DataGridViewCellComment)
' Define um comentário para a célula. Para remover era necessário
' apenas definir como String.Empty ou ""
cell.Comment = "http://vbtuga.blogspot.com/"
' Obriga a célula, agora com comentário, a actualizar
Me.DataGridView1.InvalidateCell(cell)
Um exemplo simples, que mostra como colocar um comentário numa célula, mas acima de tudo como podemos criar células personalizadas, e adiciona-las à DataGridView.
Os ciclos nas folhas de cálculo são bastante comuns quando se usa VBA. O objectivo é percorrer uma lista de valores e executar determinada acção ou validação enquanto este percorre todas as linhas ou colunas.
A execução de um ciclo simples pode ser feito da seguinte forma:
Dim x As Integer
For x = 1 To 100
Cells(x, "A").Value = x
Next
Isto irá escrever em todas as células da coluna A, da linha 1 à linha 100, um número sequencial. Mas este é um exemplo simples onde definimos onde começa e onde termina.
Para se fazer um ciclo numa lista de dados já existente, devemos sempre saber onde começar e onde terminar. Não tem lógica percorrer todas as linhas de uma folha de cálculo se estão apenas a ser utilizadas 20 ou 30.
Para se determinar a última linha usada em uma lista, devemos utilizar o seguinte método:
Dim lastRow As Long
lastRow = Cells(Cells.Rows.Count, "A").End(xlUp).Row
Isto é semelhante a seleccionar a ultima linha da coluna A e pressionar as teclas CTRL + UP ARROW, que fará saltar a selecção para a primeira célula com dados.
Só assim podemos saber com exactidão, qual a última linha utilizada, pois mesmo que existam linhas em branco na lista, todas as linhas serão percorridas.
No entanto, algumas considerações:
- Caso existam várias colunas na lista, devemos definir a coluna que tem mais dados;
- A variável utilizada para a última linha deverá ser do tipo Long e não Integer, pois uma variável do tipo Integer suporta apenas números até 32,768, sendo este inferior ao total de linhas disponível na folha de calculo.
Depois, é só utilizar a variável para limitar o ciclo:
Dim lastRow As Long
Dim x As Integer
lastRow = Cells(Cells.Rows.Count, "A").End(xlUp).Row
For x = 1 To lastRow
Debug.Print(Cells(x, "A").Value)
Next
Isto irá listar o valor de todas as células na coluna A. Para verificar qual a última coluna utilizada, o método é semelhante:
Dim lastColum As Integer
lastColum = Cells(1, Cells.Columns.Count).End(xlToLeft).Column
Mas existem outros métodos de verificar a última linha ou coluna utilizada, usando, por exemplo, o método SpecialCells:
Dim lastCellRow As Long
Dim lastCellColumn As Long
' Informação da ultima linha
lastCellRow = ActiveSheet.Cells.SpecialCells(xlLastCell).Row
' Informação da ultima coluna
lastCellColumn = ActiveSheet.Cells.SpecialCells(xlLastCell).Column
No entanto, existem vários registos de que este método não é fiável, e basta apagar algumas linhas para verificar que não o é, e por isso não é recomendado.
Existe ainda uma especial atenção para ciclos onde são eliminadas linhas ou colunas, onde o ciclo deverá ser efectuado do fim para o principio, ou seja, da última linha/colunas para a primeira. Deste modo, um ciclo para eliminar linhas, por exemplo, deverá ser feito da seguinte forma:
Dim lastRow As Long
Dim x As Integer
lastRow = Cells(Cells.Rows.Count, "A").End(xlUp).Row
' Ciclo que irá percorrer da última para a primeira linha
For x = lastRow To 1 Step -1
' Caso a célula esteja vazia
If Len(Cells(x, "A").Value) = 0 Then
Rows(x).Delete()
End If
Next
Estes são alguns exemplos simples de como fazer ciclos em células e como verificar qual a última linha ou coluna utilizada.
É muito importante quando se desenvolve alguma macro, que o tempo de execução não seja exagerado, e até porque, as folhas de calculo têm normalmente diversos cálculos, objectos, formatações, etc, o que pode tornar as acções mais lentas.
Existem no entanto pequenos “truques” que permitem melhorar reduzir o tempo de execução das macros e melhorar o aspecto visual quando estas correm.
Application.ScreenUpdating
Esta propriedade define se o Excel actualiza ou não a folha de cálculo. Por defeito está definida como verdadeira (True), mas podemos defini-la como falsa (False).
Por exemplo, se queremos apagar algumas linhas ou colunas individualmente, sempre que damos uma ordem para eliminar, a folha é actualizada, aumentando o tempo de execução. Se definirmos Application.ScreenUpdating = False, só quando voltarmos a definir Application.ScreenUpdating = True, é que a folha é actualizada, reduzindo significativamente o tempo de execução e o aspecto geral da macro.
No seguinte exemplo, só após o código terminar, a folha será actualizada:
Sub DeleteColumns()
Application.ScreenUpdating = False
Range("c:c").Delete
Range("b:b").Delete
Range("a:a").Delete
Application.ScreenUpdating = True
End Sub
Application.Calculation
A propriedade Calculation permite definir de que forma são feitos os cálculos. Sempre que escrevemos um valor numa célula, o Excel actualiza todos os cálculos, em todas as células, o que poderá levar algum tempo. Por defeito esta propriedade está definida como automática (xlCalculationAutomatic) mas podemos no entanto definir para não ser executada, ou seja, para manual (xlCalculationManual).
Por exemplo, e para testarmos esta opção, se tivermos na colunas A, B, C, D, E e F todas as células com uma formula para gerar um número aleatório - =Rand() -, sempre que escrevermos um valor numa célula, irão ser actualizadas todas as formulas. No seguinte exemplo será desabilitado enquanto o ciclo estiver a ser executado, e poderá testar com e sem o desabilitar dos cálculos automáticos, para ver a diferença.
Sub WriteValues()
Dim x As Integer
Application.Calculation = xlCalculationManual
For x = 1 To 100
Cells(x, "g").Value = x
Next
Application.Calculation = xlCalculationAutomatic
End Sub
Application.EnableEvents
Com explicado num artigo anterior, existem diversos eventos que estão disponíveis no Excel. Esses eventos podem executar operações demoradas e isso pode não ser desejado. Por defeito esta propriedade está definida como verdadeira (True) mas podemos desactivar quando queremos que os eventos não aconteçam.
Imagine que no evento Change, que é executado sempre que algum valor seja alterado, temos um ciclo demorado.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim x As Integer
For x = 0 To 1000
Debug.Print(x)
Next
End Sub
Com a opção Application.EnableEvents = True, sempre que escrever um valor em qualquer célula, irá correr também o código anterior.
Definindo esta propriedade como falsa, Application.EnableEvents = False, fará com que apenas se execute o nosso código e não os restantes eventos do documento.
Sub Run()
Application.EnableEvents = False
For x = 1 To 100
Cells(x, "g").Value = x
Next
Application.EnableEvents = True
End Sub
Isoladamente ou em conjunto, estas três propriedades permitem melhorar significativamente o tempo de execução de uma macro e com isso tornar a nossa folha de cálculo mais atractiva.
NOTA: é preciso ter algum cuidado com a utilização destas propriedades, e garantir que são sempre repostas para as definições originais, correndo-se o risco de a folha não trabalhar como está planeado. Se colocarmos, por exemplo, a propriedade Application.EnableEvents = False e não voltarmos a colocar a True, todos os eventos ficaram congelados.
Para breve mais dicas de como melhorar o código em VBA!