Visual Basic em Português

Página pessoal de Jorge Paulino sobre o Visual Basic (VB.NET, ASP.NET, VB6, VBA) e algumas noticias de tecnologia

VB.NET: Dicas de Programação #6

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!


VB6: Guardar Configurações - INI vs Registry

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!

Excel: Criação de Fórmulas Personalizadas

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!


VB.NET: Dicas de Programação #5

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!




Microsoft Office Especialist

Membro da Comunidade
Experts-Exchange


Administ. da Comunidade
Portugal-a-Programar



Twitter

Artigos no CodeProject

Artigos no CodeProject

Subscrever Novidades

Endereço de Email:

Delivered by FeedBurner

Seguidores

Histórico