DateTimePicker – Sem Selecção (nulo)
O DateTimePicker é um excelente controlo
para seleccionar e mostrar datas ou horas. Não necessita de validação, é fácil de
seleccionar ou incrementar, permite uma introdução directa e é visualmente bastante
agradável. Mas, e como não há regra sem excepção, tem um grande defeito: não tem
uma possibilidade directa simples de indicar que não existe escolha, ou seja, colocar
uma selecção a nulo.
Quando estamos a inserir ou a mostrar
dados, o controlo DateTimePicker indica sempre uma data/hora. Isto obriga a uma
adaptação por parte do programador, criando um conjunto de soluções alternativas,
para indicar que não existe nenhuma selecção. Há quem coloque uma data (tipo 01-01-1900),
há quem coloque uma checkbox para habilitar/desabilitar
o controlo e há quem simplesmente não o utilize.
A melhor solução, e é obviamente a
minha opinião, é mostrar sem nada (em branco), indicando que não tem selecção. Para
fazer isto é necessário o seguinte:
DATA
Para mostrar a data em branco é necessário
definir o controlo DateTimePicker com o formato
Custom (personalizado) e indicar um espaço em branco (“ “)
Me.DateTimePicker1.Format = DateTimePickerFormat.Custom
Me.DateTimePicker1.CustomFormat = " "
Depois, e quando o utilizador seleccionar
uma data, repomos o formato normal
Private Const EmptySpace
As String = " "
'
Detecta que foi alterado um valor(data) no DateTimePicker
Private Sub DateTimePicker1_ValueChanged(ByVal sender As
Object, ByVal
e As System.EventArgs) _
Handles DateTimePicker1.ValueChanged
' Verifica se o texto
é um espaço em branco
If DateTimePicker1.Text
= EmptySpace Then
' Define
o formato como Shot (formato DD-MM-YYYY) e dá um
' enter
para fechar (CloseUp) da janela que está aberta
Me.DateTimePicker1.Format = DateTimePickerFormat.Short
SendKeys.Send("{ENTER}")
End If
End
Sub
HORA
Como no caso anterior é necessário
definir o controlo DateTimePicker com o formato
Custom (personalizado) e indicar um espaço em branco (“ “). O problema é
que neste caso o utilizador inicia a alteração da hora/ através dos botões UpDown
uma vez que não consegue introduzir manualmente (não permite o focus uma vez que
não tem valores). Para detectarmos que o utilizador clicou com o rato num botão
de incremento/decremento é necessário fazer o override ao WndProc, uma vez que os
eventos MouseDown ou MouseUp não detectam os botões.
O melhor é criar um controlo personalizado
em que se define este processo. No exemplo o controlo tem o nome de myDateTimePicker.
Me.MyDateTimePicker1.ShowUpDown
= True
Me.MyDateTimePicker1.Format
= DateTimePickerFormat.Custom
Me.MyDateTimePicker1.CustomFormat = " ""
O código para o controlo personalizado,
fazendo o override ao WndProc é o seguinte:
Public Class
myDateTimePicker
Inherits DateTimePicker
Const EmptySpace As
String = " "
'
Constante que verifica se foi utilizado o botão esquerdo do rato
Private Const WM_LBUTTONDOWN = &H201
' Overrides ao WndProc
Protected Overrides
Sub WndProc(ByRef m
As System.Windows.Forms.Message)
' Caso seja o botão esquerdo e o texto esteja com um espaço em branco
If m.WParam = WM_LBUTTONDOWN Then
If Me.Text
= EmptySpace Then
Me.Format = DateTimePickerFormat.Time
End If
End If
MyBase.WndProc(m)
End
Sub
End
Class
Para terminar,
pode-se ainda apagar a formatação quando o utilizador pressionar a tecla DEL, nos
eventos KeyUp, KeyDown ou KeyPress.
Private Sub MyDateTimePicker1_KeyDown(ByVal sender As
Object, ByVal
e As System.Windows.Forms.KeyEventArgs) _
Handles
MyDateTimePicker1.KeyDown
' Caso a tecla seja a
DELETE
If e.KeyCode = Keys.Delete Then
Me.MyDateTimePicker1.Format = DateTimePickerFormat.Custom
Me.
MyDateTimePicker1.CustomFormat = " "
End If
End
Sub
Este é um exemplo
de como melhorar a utilização deste controlo tornando-o quase “perfeito”.
StopWatch – Medindo
o Tempo de Execução
Existem formas diferentes de construir
código de modo a executar algo. Umas mais rápidas, umas mais simples ou mesmo mais
versáteis. As operações mais demoradas estão normalmente relacionadas com a gestão
de dados (bases de dados, ficheiros, etc.) mas as pequenas optimizações em conjunto,
podem melhorar a performance da aplicação. Nos casos de duvida, em que qual é o
método mais rápido para fazer algo, o melhor mesmo é testar.
O StopWatch permite medir de uma forma
bastante simples o tempo de execução, utilizando muito pouco código. O método para
testar o tempo de execução de diferentes métodos é sempre o mesmo, ou seja:
.
' Cria-se uma nova instância
Dim stopWatch
As New Stopwatch
' Inicia-se a contagem
stopWatch.Start()
' Faz-se um ciclo com várias repetições
para verificar o tempo que demorou
' a executar. Caso o código já seja
demorado, não é necessário ciclo
For x As Integer
= 0 To 10000
' código a testar
Next
' Para-se a contagem
stopWatch.Stop()
' Mostra-se o tempo que demorou a executar
em milisegundos
Debug.WriteLine(stopWatch.ElapsedMilliseconds.ToString)
Depois repetem-se
algumas vezes, coloca-se o código de comparação a testar e comparam-se os resultados
obtidos. Pode ser necessário ajustar o número de repetições no ciclo de maneira
a diferenciar melhor os resultados.
Podem-se ainda
melhorar a visualização, especialmente em tarefas mais demoradas, utilizando o TimeSpan.
Depois de parar o Stopwatch, através do método Stop(), pode-se fazer o seguinte:
' Coloca o tempo total na
variável do tipo TimeSpan.
Dim ts As
TimeSpan = stopWatch.Elapsed
' Mostra a informação dividida
em Horas, Minutos, Segundo e Milisegundos.
Dim totalTime As
String = _
String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
Debug.WriteLine(totalTime)
O exemplo mostrar como aplicar e executar
testes de velocidade de código, com o objectivo de melhorar a performance da aplicação.
PS: Como sempre, qualquer dúvida,
comentário ou correcção ao artigo é sempre bem
vinda!
A maioria das aplicações tem algumas
opções que são importantes guardar de modo a serem utilizadas durante e em próximas
execuções. Estas configurações ou settings
são bastante utilizadas e importantes, sendo os métodos mais usados no Visual Basic
6 a criação de ficheiros INI’s e a utilização do
Registry do Windows.
Tamanho e/ou localização de uma janela,
data do último acesso, impressora utilizada, nome do último
login, são apenas alguns exemplos do que é “normal” e necessário guardar.
Pode-se obviamente também guardar esta informação numa base de dados, mas se necessitarmos
de guardar o endereço da base de dados ou o nome do servidor? É necessário recorrer
a outros métodos.
Actualmente, e embora também se utilize
também no Visual Basic 6, os ficheiros XML estão a liderar os ficheiros de
settings fazendo parte de várias classes do Visual Basic.NET o que simplifica
bastante este processo. Existe ainda um processo de criação de
settings embebido na aplicação.
Existem grandes defensores dos ficheiros
INI’s e grandes defensores de guardar a informação no
Registry do Windows, no entanto, e é a minha opinião pessoal, é que ambos
os métodos são válidos e devem ser utilizados para diferentes fins.
Ambos têm vantagens e desvantagens,
por exemplo: os ficheiros INI’s são fáceis analisar, não necessitam de acesso de
administrador para os modificar/criar, mas também são fáceis de modificar não oferecendo
por isso nenhuma segurança. Por outro lado a utilização do
Registry do Windows necessita de acesso de administrador (o que em muitos
sistemas é impensável), é mais delicada a sua utilização pois podem-se apagar/modificar
inadvertidamente outras informações importantes no
Registry e é mais difícil de analisar a informação sendo necessário recorrer
a um editor. No entanto é bastante mais seguro.
Podem ser utilizados os dois sistemas,
de acordo com a interpretação de cada programador, por exemplo, guardar informações
mais importantes (como passwords) no
Registry e informações menos importantes/relevantes
em ficheiros INI.
Ficheiros INI’s
Os ficheiros INI’s não são mais do
que ficheiros de texto em que se guardam informações devidamente agrupadas o que
permitem uma boa organização e fácil acesso através de API’s. A estrutura de um
ficheiro é:
[Secção1]
Chave1=Texto1
Chave2=Texto2
[Secção2]
Chave1=Texto1
Chave2=Texto2
Este é um exemplo simples de utilização:
Option Explicit
' Declaração de API's
Private Declare Function WritePrivateProfileString Lib
"kernel32" Alias "WritePrivateProfileStringA" _
(ByVal
lpApplicationName As String,
ByVal lpKeyName As Any, _
ByVal lpString As Any,
ByVal lpFileName As String)
As Long
Private Declare Function GetPrivateProfileString Lib
"kernel32" Alias "GetPrivateProfileStringA" _
(ByVal
lpApplicationName As String,
ByVal lpKeyName As Any, _
ByVal lpDefault As String,
ByVal lpReturnedString As String, _
ByVal nSize As Long,
ByVal lpFileName As String)
As Long
' Escreve no ficheiro
INI através de API ignorando o erro
Public Function INIWrite(sSection As String,
sKeyName As String, sNewString
As String, sINIFileName As String) As Boolean
Call
WritePrivateProfileString(sSection, sKeyName, sNewString, sINIFileName)
INIWrite = (Err.Number = 0)
End Function
' Lê no ficheiro INI
através de API ignorando o erro
Public Function INIRead(sSection As String,
sKeyName As String, sINIFileName
As String) As String
Dim
sRet As String
sRet = String(255, Chr(0))
INIRead = Left(sRet, GetPrivateProfileString(sSection,
ByVal sKeyName, "", sRet, Len(sRet), sINIFileName))
End Function
' No evento Form Load
Private Sub Form_Load()
' Escreve uma informação no ficheiro INI. Caso exista modifica
a existente,
‘ caso não exista cria uma nova chave e o respectivo texto
Call INIWrite("GERAL", "utilizador", "jpaulino", App.Path
& "\mySettings.ini")
End Sub
' Botão para mostrar
o resultado
Private Sub btnRead_Click()
Dim
result As String
' Lê a chave no ficheiro INI para uma variável
e mostra a informação
result = INIRead("GERAL", "utilizador", App.Path &
"\mySettings.ini")
MsgBox result, vbInformation
End Sub
O resultado no evento Form Load será
a criação de um ficheiro ini (mySettings.ini) com a seguinte estrutura:
[GERAL]
utilizador=jpaulino
Windows Registry
A utilização do Windows Registry é
ainda mais fácil, não sendo necessário a utilização de API’s. A localização onde
é guardada a informação é fixa e é a seguinte:
HKEY_CURRENT_USER
\Software
\VB and VBA Program Settings
\Applicação
\ Secção
Chave = Texto
Este é um exemplo simples de utilização:
Option Explicit
' Escreve a informação
no Registry
Private Sub bntWrite_Click()
SaveSetting "Teste", "GERAL",
"Página", "http://vbtuga.blogspot.com/"
End Sub
' Botão para mostrar
o resultado
Private Sub btnRead_Click()
Dim
result As String
result = GetSetting("Teste",
"GERAL", "Página", "")
MsgBox result, vbInformation
End Sub
Como já referido anteriormente, existem
actualmente outros métodos para guardar informação relevante à aplicação, mas estas
duas formas são ainda utilizadas. Muitos programadores de Visual Basic 6 que migraram
para Visual Basic.Net ainda utilizam estes métodos (embora não aconselhável).
Este artigo, embora bastante simples,
pretende apenas mostrar algumas alternativas fáceis e eficazes de guardar informações
de configuração da aplicação e como as utilizar de uma forma geral.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
O Microsoft Excel tem inúmeras fórmulas para diversos utilizações, muitas delas que nem sabemos da sua existência nem sabemos para que serve. Tem fórmulas para quase tudo! Mas no entanto existem operações que por serem muito específicas ou simplesmente para simplificar (de modo a não utilizar muitas fórmulas em conjunto) dava jeito serem personalizadas, ou seja, à nossa medida.
A verdade é que não é prático utilizar algo do género:
{=IF(FORMULA(<criteria>)=0;IF(MATCH(<range>;<criteria>); ....
A fórmula além de complicada de construir é também complicada de interpretar e alterar. Nestes casos o ideal é criarmos a nossa própria fórmula permitindo, além de simplificar, executar mais funcionalidades para além das já existentes e disponíveis.

Para demonstrar a criação de uma fórmula personalizada, será mostrado um exemplo de como recolher a primeira e ultima palavra de um nome completo, construindo um nome abreviado.
Nota: Esta funcionalidade poderá também ser construída através de fórmulas, mas o objectivo é mostrar a melhoria que estas fórmulas predefinidas podem trazer às folhas de cálculo.
Para adicionarmos esta função a uma folha de Excel devemos fazer o seguinte:
1 – Menu Tools – Macros – Visual Basic Editor ou ALT+F11
2 – Adicionar um novo módulo clicando com o botão direito ou através do menu Insert (Figura 1)
(Figura 1)
3 – Colocar a seguinte código no módulo criado:
' ---------------------------------------------------------------------------
' Separa uma frase e retorna o primeiro e último nome
' ---------------------------------------------------------------------------
Public Function FirstLast(ByVal str As String) As String
Dim result() As String
Dim total As Integer
' Separa para um array o valor da célula de acordo com o
' separador " " ou seja espaço
' Por exemplo na frase "isto é um teste" o resultado ficará desta forma:
'
' result(0) = isto
' result(1) = é
' result(2) = um
' result(3) = teste
'
result = Split(str, " ")
' Verifica qual o tamanho do array (o array começa em zero)
total = UBound(result)
' Caso seja zero
If total = 0 Then
If str = "" Then
' Caso esteja em branco não retorna nada
FirstLast = ""
Else
' Caso exista texto retorna esse texto
FirstLast = StrConv(str, vbProperCase)
End If
Else
' Retorna o primeiro nome e o último, com um espaço no
‘ meio utiliza ainda a função StrConv() que converte
‘ o texto (neste caso) para um formato the Proper Case
FirstLast = StrConv(result(0), vbProperCase) & Space(1) & _
StrConv(result(total), vbProperCase)
End If
End Function
4 – Fechar o Editor de VBA e na folha de cálculo utilizar a função criada
O exemplo deste pequeno artigo é mostrar que as fórmulas personalizadas, utilizando VBA, permitem melhorar e potenciar as folhas de cálculo. É mostrado um exemplo simples que pretende explicar o funcionamento geral mas também mostrar algumas instruções de código (como o caso de arrays, StrConv, Split, etc).
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
DataGridView – Actualização imediata após selecção na ComboBox
O controlo DataGridView permite definir diversos tipos para as suas células, além de se poder inserir novos controlos nas células. A DataGridViewComboBoxCell é um desses tipos e permite ao utilizador efectuar um diverso número de escolhas para cada registo.
Para detectar uma alteração é necessário utilizar o evento EditingControlShowing em conjunto com a DataGridViewComboBoxEditingControl (que representa a ComboBox alojada na célula). Sem a utilização deste evento é possível também detectar o valor através do evento CellValueChanged mas para isso é necessário mudar de célula para que o evento seja disparado.
Este exemplo mostra como implementar isto, permitindo sempre que se altere um item na ComboBox (através do SelectedIndexChanged) execute uma acção, que neste caso é a actualização do um valor numa célula.
Tratasse de uma lista de produtos com nomes e valores aleatórios em que, ao ser alterado o valor de desconto, altera também o valor do total do artigo.
Private WithEvents dgvCombo As DataGridViewComboBoxEditingControl
Private Sub dgvCombo_SelectedIndexChanged(ByVal sender As Object, _
ByVal e As System.EventArgs)
' Caso o valor seja Nothing sai do procedimento
If dgvCombo Is Nothing Then Exit Sub
' Caso tenha sido alterado a coluna da ComboBox
If Me.DataGridView1.CurrentCell.ColumnIndex = 4 Then
Dim row As Integer = Me.DataGridView1.CurrentCell.RowIndex
' Guarda a informação do preço
Dim preco As Double = Me.DataGridView1(2, row).Value
Dim desc As Double = dgvCombo.Text.ToString.Replace("%", "")
' Caso o calor seleccionado não seja > 0
If desc > 0 Then
' Calcula o preço com o desconto e actualiza o campo "Total"
Me.DataGridView1(3, row).Value = preco - (preco * (desc / 100))
Else
' Actualiza o campo "Total" sem desconto
Me.DataGridView1(3, row).Value = preco
End If
End If
End Sub
' Define a dgvCombo = Nothing para funcionar na próxima ComboBox
Private Sub DataGridView1_CellLeave(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
Handles DataGridView1.CellLeave
dgvCombo = Nothing
End Sub
' Quando um controlo é editado
Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) _
Handles DataGridView1.EditingControlShowing
' Verifica se o control editado foi a ComboBox
If TypeOf e.Control Is DataGridViewComboBoxEditingControl Then
' Atribui o controlo
If dgvCombo Is Nothing Then
dgvCombo = e.Control
End If
' Atribui um evento quando é alterado o a selecção
AddHandler dgvCombo.SelectedIndexChanged, _
New EventHandler(AddressOf dgvCombo_SelectedIndexChanged)
End If
End Sub
O objectivo é mostrar como executar uma acção de imediato após a selecção de item numa combobox, facilitando a utilização deste controlo.
ListView – Criar coluna de hyperlinks detectando a coluna clicada
A ListView não permite inserir um tipo na coluna específico para hiperligações. Para conseguirmos criar uma coluna personalizada de hiperligações é necessário detectar qual a coluna que foi clicada e também alterar o ponteiro do rato na coluna especifica. No entanto, e para detectar a coluna clicada ou que o ponteiro do rato está sobre ela, é necessário que não esteja visível a scrollbar, ou seja, as colunas têm de estar visíveis.

Este exemplo mostra como criar a coluna personalizada, sendo introduzidos dados aleatórios e um dos SubItems da lista estar definido com a cor da fonte a azul. Não esquecer de que é necessário definir no item principal o UseItemStyleForSubItems = False para que seja possível definir vários formatos em diversos SubItems.
''' <summary>
''' Verifica qual a coluna onde o ponteiro do rato está
''' </summary>
''' <param name="e">MouseEventArgs</param>
Private Function lvColumnNumber( _
ByVal e As System.Windows.Forms.MouseEventArgs) As Integer
Dim colend As Integer = 0
Dim colstart As Integer = 0
Dim x As Integer
' Ciclo nas colunas
For x = 0 To (ListView1.Columns.Count - 1)
' Verifica qual a largura da coluna
colend = colend + ListView1.Columns(x).Width
If colstart <= e.X And e.X <= colend Then
Return x + 1
End If
colstart = colstart + ListView1.Columns(x).Width
Next
End Function
' Quando é feito um clique na lista
Private Sub ListView1_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles ListView1.MouseDown
' Verifica se a coluna é a dos URL's
If lvColumnNumber(e) = 2 Then
' Verifica qual o item onde foi feito o clique
Dim item As ListViewItem = Me.ListView1.GetItemAt(e.X, e.Y)
' Caso tenha sido sobre um item
If item IsNot Nothing Then
' Verifica qual a coluna e lança um novo processo
' com o URL da lista, abrindo o explorador por defeito
Dim col As Integer = lvColumnNumber(e) - 1
Process.Start(item.SubItems(col).Text)
End If
End If
End Sub
' Quando o ponteiro do rato está sobre a lista
Private Sub ListView1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseMove
' Verifica se a coluna é a dos URL's
If lvColumnNumber(e) = 2 Then
Windows.Forms.Cursor.Current = Cursors.Hand
Else
Windows.Forms.Cursor.Current = Cursors.Default
End If
End Sub
Deste modo é possível criar uma coluna com uma aparência diferente e também detectar qual a coluna que foi clicada.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!