Usando formulários com classe – parte 1: classe
O título deste artigo tem duplo sentido de forma
intencional. Aqui veremos como usar formulários com classe usando classes. Se
você ainda não leu o artigo sobre classes ou quer revisá-lo antes de continuar,
segue o link. Usar classes é fazer uso do que a programação orientada a objetos
tem de melhor.
Antes de mais nada, é preciso entender como projetar para
que esse processo seja usado de forma eficiente. Precisamos definir a função de
cada um: o que o formulário fará, o que a classe fará e quem vai se comunicar
com quem. A imagem abaixo deixa isso de forma mais clara:
Através da imagem vemos que a classe fará a ponte entre o
formulário e a planilha. Quais as vantagens de ser feito dessa forma? Em primeiro lugar, o principal objetivo da
programação orientada a objetos é reaproveitamento de código. Uma classe bem
estruturada pode ser reaproveitada em outras partes do projeto ou em outros
projetos. Um formulário se comunicando diretamente com a planilha dificilmente
será reaproveitável. Já a classe poderá ser reaproveitada com pequenas
alterações. Uma outra vantagem de usar classes é que você pode fazer a
consistência de valores nela própria, não no formulário, mantendo assim toda a
inteligência em um só lugar e aumentando a quantidade de código que poderá ser
reaproveitado com a classe.
Suponha um exemplo onde você tem uma planilha com dados de
clientes, fornecedores e funcionários. Em todos os casos teremos código, nome,
documento de identificação (CPF/CNPJ) e mais alguns dados específicos de cada
um. Os métodos e funções de cada classe provavelmente serão os mesmos:
adicionar, pesquisar, alterar e desativar (exclusão lógica; bem melhor que
exclusão física - explicarei mais adiante). Ao fazer uma classe com suas
propriedades e métodos comunicando com a respectiva planilha você pode replicar
boa parte do código para as outras classes, bastando alterar, adicionar ou
remover algumas propriedades e refletindo essas alterações nos métodos e
funções. Depois basta desenvolver o novo formulário e criar suas ligações com a
respectiva classe. Além disso, há a possibilidade de testar a classe com
sub-rotinas de teste, sem ter o formulário ainda pronto.
No caso de fazer um formulário comunicando diretamente com a
planilha até dá para copiar o formulário e fazer as devidas alterações, mas
será preciso muita atenção. O problema é que as pessoas acabam dando muita
atenção à aparência primeiro e depois pensando na funcionalidade. Com uma
classe específica pronta e devidamente testada, basta fazer as conexões do
formulário com essa classe, sem muita necessidade de código no formulário, pois
a maior parte das funcionalidades está na classe. Os eventos do formulário
basicamente chamarão os métodos e as funções da classe ou definirão os valores
de suas propriedades.
Vamos usar um exemplo simples de um formulário de
funcionários, onde temos as seguintes propriedades: número registro do
funcionário, nome completo, data de nascimento, CPF, cargo e salário. A
planilha terá o nome Funcionários e o
nome de código (codename) PlFuncionarios. Se não entendeu ou não
sabe onde definir esses nomes, veja a imagem abaixo, é autoexplicativa:
Com o nome de código definido temos a vantagem de não
precisar usar uma variável do tipo Sheet
para fazer a referência à planilha. Além disso, caso o usuário renomeie a
planilha (o nome que aparece para ele no Excel) o funcionamento não sofrerá
interferência, já que a referência será feita via codename.
Crie a planilha e faça o cabeçalho, conforme a imagem
abaixo:
Em seguida, crie um módulo de classe chamado clsFuncionario e acrescente o seguinte
código das propriedades:
Private pRegistro As
Long ' Registro do funcionário
Private pNome As
String ' Nome completo
Private pDataNascimento As Date ' Data de nascimento
Private pCPF As
String * 11 ' CPF
Private pCargo As
Integer ' Código do cargo
Private pSalario As
Currency ' Salário
Private pAtivo As
Boolean ' Indicador de ativo
Private pInconsistencia As Boolean ' Indicador de dado inconsistente
' Registro: somente leitura
Public Property Get Registro() As Long
Registro = pRegistro
End Property
' Nome: leitura e escrita
Public Property Get Nome() As String
Nome = pNome
End Property
Public Property Let Nome(Valor As String)
pNome = Valor
pInconsistencia =
False
End Property
' DataNascimento: leitura e escrita
Public Property Get DataNascimento() As Date
DataNascimento = pDataNascimento
End Property
Public Property Let DataNascimento(Valor As Date)
pDataNascimento =
Valor
pInconsistencia =
False
End Property
' CPF: leitura e escrita
Public Property Get CPF() As String
CPF = pCPF
End Property
Public Property Let CPF(Valor As String)
Valor = Trim(Replace(Replace(Valor, ".", ""), "-", ""))
If Len(Valor) < 11 Then
Valor = String(11 - Len(Valor), "0") & Valor
End If
If ValidarCPF(Valor) Then
pCPF = Valor
pInconsistencia = False
Else
pCPF = 0
pInconsistencia = True
End If
End Property
CPF = pCPF
End Property
Public Property Let CPF(Valor As String)
Valor = Trim(Replace(Replace(Valor, ".", ""), "-", ""))
If Len(Valor) < 11 Then
Valor = String(11 - Len(Valor), "0") & Valor
End If
If ValidarCPF(Valor) Then
pCPF = Valor
pInconsistencia = False
Else
pCPF = 0
pInconsistencia = True
End If
End Property
' Funcao: leitura e escrita
Public Property Get Cargo() As Integer
Cargo = p Cargo
End Property
Public Property Let Cargo(Valor As Integer)
If Valor > 0 Then
pCargo = Valor
pInconsistencia =
False
Else
pCargo = 0
pInconsistencia =
True
End If
End Property
' Salario: leitura e escrita
Public Property Get Salario() As Currency
Salario = pSalario
End Property
Public Property Let Salario(Valor As Currency)
If Valor > 0 Then
pSalario = Valor
pInconsistencia =
False
Else
pSalario = 0
pInconsistencia =
True
End If
End Property
' Ativo: somente leitura
Public Property Get Ativo() As Boolean
Ativo = pAtivo
End Property
' Inconsistencia: somente leitura
Public Property Get Inconsistencia() As Long
Inconsistencia =
pInconsistencia
End Property
Caso você tenha lido (ou relido) o artigo sobre classes deve
entender todo o código (ou pelo menos uma boa parte). Se fez uma análise entre
o enunciado e o código, percebeu que acrescentei duas propriedades booleanas somente
leitura chamadas Ativo e Inconsistencia:
- Ativo, como o
nome diz, servirá para dizer se o registro está ativo. Quando essa propriedade valer
verdadeiro significa que o registro pode ser usado sem problemas, quando valer
falso significa que não pode ser usada porque foi "excluída". Isso é
uma exclusão lógica, ou seja, o registro está inativo e não deve ser utilizado.
Exclusões físicas de dados costumam dar muita dor de cabeça para recuperar os
dados porque sempre haverá algum usuário agindo como tal. A exclusão lógica
previne que um registro seja de fato apagado, podendo aparecer em históricos e
até mesmo ser recuperado, bastando alterar o status desta propriedade;
- Inconsistencia servirá
para indicar ao formulário (ou qualquer outro objeto que instancie essa classe)
que o valor enviado para a propriedade é inválido e não foi armazenado. Perceba
que a variável pInconsistencia é
atualizada em todas as Property Let,
mantendo essa propriedade atualizada sempre que um novo valor for associado a
alguma propriedade da classe. Nas propriedades que há validação de dados pInconsistencia pode receber verdadeiro
ou falso, nas que não há validação recebe apenas falso para não interferir alguma
checagem dessa propriedade mal colocada.
As propriedades Cargo e Salario validam se recebem valores positivos diferentes de zero, caso contrário ligam o indicador de inconsistência. Já Nome e DataNascimento apenas guardam o dado, sem efetuar consistências.
Note que a propriedade CPF está como uma string de 11 dígitos. Uma variável do tipo long armazena até 2.147.483.647, ou seja, não consegue armazenar todos os 11 dígitos. Na escrita (Let) é primeiro removido eventuais pontos e hífens, depois o conteúdo é transformado em 11 dígitos se necessário e por fim é enviado para a função ValidarCPF, que primeiramente deve checar se o valor recebido é numérico antes de conferir se o DAC é válido para o número.
As propriedades Cargo e Salario validam se recebem valores positivos diferentes de zero, caso contrário ligam o indicador de inconsistência. Já Nome e DataNascimento apenas guardam o dado, sem efetuar consistências.
Note que a propriedade CPF está como uma string de 11 dígitos. Uma variável do tipo long armazena até 2.147.483.647, ou seja, não consegue armazenar todos os 11 dígitos. Na escrita (Let) é primeiro removido eventuais pontos e hífens, depois o conteúdo é transformado em 11 dígitos se necessário e por fim é enviado para a função ValidarCPF, que primeiramente deve checar se o valor recebido é numérico antes de conferir se o DAC é válido para o número.
A propriedade Registro é somente leitura, ou seja, não podemos associar um valor diretamente à propriedade. Como é que vamos definir um valor novo? Através de uma função chamada Pesquisa. Essa função, como o próprio nome entrega, fará a pesquisa na tabela pelo Registro, recebendo o valor na função e retornando verdadeiro ou falso. Se verdadeiro, também carrega os valores em suas respectivas propriedades. Ao contrário do que possa parecer, essa forma é feita para simplificar. Imagine que devêssemos definir o valor de Registro e depois executar a função Pesquisa:
Funcionario.Registro = txtRegistro.Value
Funcionario.Pesquisar
Colocando o Registro
como parâmetro na função Pesquisar reduz
uma linha:
Funcionario.Pesquisar(txtRegistro.Value)
Essa função Pesquisar
deverá saber o intervalo atual da tabela de funcionários, guardar o número do
registro (fornecido como parâmetro) na propriedade Registro da classe e pesquisar essa coluna para saber se o valor
existe na base. Se existir, irá armazenar o número da linha em pLinha e compor as propriedades com seus
respectivos valores, além de retornar o valor verdadeiro. Caso não exista, o
valor de pLinha será o número da
última linha mais um, já preparando a classe para adicionar um eventual novo
registro e retornará falso. Compreendido o funcionamento da função, segue o código:
Public Function Pesquisar(Registro As Long) As Boolean
Dim UltimaLinha As
Long
Dim Intervalo As Range
Dim Encontrado As Range
Dim NumCPF As String
Dim NumCPF As String
pRegistro = Registro
PlFuncionarios.Activate
UltimaLinha =
PlFuncionarios.Cells.Find("*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows,
SearchDirection:=xlPrevious).Row
Set Intervalo =
PlFuncionarios.Range(Cells(1, 1), Cells(UltimaLinha, 1))
Set Encontrado =
Intervalo.Find(pRegistro, LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByColumns)
If Encontrado Is
Nothing Then
plinha =
UltimaLinha + 1
Pesquisar = False
Else
pLinha =
Encontrado.Row
pNome = PlFuncionarios.Cells(pLinha,
2).Value
pDataNascimento =
PlFuncionarios.Cells(pLinha, 3).Value
NumCPF = PlFuncionarios.Cells(pLinha, 4).Value
If Len(NumCPF) < 11 Then
pCPF = String(11 - Len(NumCPF), "0") & NumCPF
Else
pCPF = NumCPF
End If
If Len(NumCPF) < 11 Then
pCPF = String(11 - Len(NumCPF), "0") & NumCPF
Else
pCPF = NumCPF
End If
pCargo =
PlFuncionarios.Cells(pLinha, 5).Value
pSalario =
PlFuncionarios.Cells(pLinha, 6).Value
pAtivo =
PlFuncionarios.Cells(pLinha, 7).Value
Pesquisar = True
End If
End Function
Você pode testar a função incluindo um funcionário
manualmente e criando uma rotina de teste que exiba os dados quando encontrado
ou exiba uma mensagem quando não encontrado. Pode fazer essa rotina como um
exercício, vale a pena testar uma função ou método de classe tão logo esteja
pronto.
Veja que é na pesquisa que leremos o valor do CPF, que é armazenado como número na planilha e, portanto, o Excel remove os zeros à esquerda. Para trazer o número com 11 dígitos é preciso fazer a verificação que está na rotina e preencher os zeros conforme a necessidade.
Veja que é na pesquisa que leremos o valor do CPF, que é armazenado como número na planilha e, portanto, o Excel remove os zeros à esquerda. Para trazer o número com 11 dígitos é preciso fazer a verificação que está na rotina e preencher os zeros conforme a necessidade.
As outras funcionalidades precisarão que Pesquisar tenha sido executada, para os
valores de Registro e pLinha estejam devidamente preenchidos.
Caso contrário, é possível que pLinha
tenha valor zero e uma mensagem de erro surgirá ao tentar acessar a linha zero
da planilha, que não existe. Para prevenir isso, vamos criar uma função privada
que retornará verdadeiro quando os valores sejam positivos e diferentes de
zero, caso contrário retornará falso:
Private Function ValidarPosicao() As Boolean
If pRegistro >= 0
And pLinha >= 0 Then
ValidarPosicao =
True
Else
ValidarPosicao =
False
End If
End Function
Note que essa função é privada, ou seja, somente será
acessada internamente pela própria classe. Podemos então passar às outras
funcionalidades. Poderíamos construir como métodos, que não retornam valores,
mas vamos podemos construir como funções, retornando verdadeiro para indicar
que processou conforme o esperado e falso para indicar que não processou por
algum problema, como a função ValidarPosicao
retornando falso (afinal, é para isso mesmo que ela foi criada). Segue o código
dessas funções:
Public Function Adicionar() As Boolean
If ValidarPosicao =
False Then
Adicionar = False
Else
PlFuncionarios.Cells(pLinha, 1).Value = pRegistro
PlFuncionarios.Cells(pLinha, 2).Value = pNome
PlFuncionarios.Cells(pLinha, 3).Value = pDataNascimento
PlFuncionarios.Cells(pLinha, 4).Value = pCPF
PlFuncionarios.Cells(pLinha, 5).Value = p Cargo
PlFuncionarios.Cells(pLinha, 6).Value = pSalario
PlFuncionarios.Cells(pLinha, 7).Value = True
Adicionar = True
End If
End Function
Public Function Alterar() As Boolean
If ValidarPosicao =
False Then
Alterar = False
Else
PlFuncionarios.Cells(pLinha, 2).Value = pNome
PlFuncionarios.Cells(pLinha, 3).Value = pDataNascimento
PlFuncionarios.Cells(pLinha, 4).Value = pCPF
PlFuncionarios.Cells(pLinha, 5).Value = p Cargo
PlFuncionarios.Cells(pLinha, 6).Value = pSalario
Alterar = True
End If
End Function
Public Function Ativar() As Boolean
If ValidarPosicao =
False Then
Ativar = False
Else
PlFuncionarios.Cells(pLinha, 7).Value = True
Ativar = True
End If
End Function
Public Function Desativar() As Boolean
If ValidarPosicao =
False Then
Desativar = False
Else
PlFuncionarios.Cells(pLinha, 7).Value = False
Desativar = True
End If
End Function
Crie as rotinas de teste para as funções acima como
exercício, colocando em um módulo exclusivo para esse fim. Ponha um nome como TesteClasseFuncionario. Tenha o hábito
de sempre testar cada método ou função logo após ter codificado, isso garante
que você testará com a lógica ainda fresca na cabeça, permitindo encontrar
eventuais erros mais facilmente. Também recomendo manter o módulo de testes, para testar eventuais alterações futuras no código ou mesmo para ter rotinas de teste prontas quando replicar a classe em outro lugar.
.
Perceba que não inclui ainda a função ValidarCPF e por isso dará erro ao tentar associar um valor à
propriedade CPF. Você pode codificar
essa parte como exercício (a forma de calcular o DAC é facilmente encontrado na internet) antes de seguir adiante. Você também exercitar criando um método privado para ordenar a
tabela e chamá-lo no final da função Adicionar. Os códigos seguem abaixo, mas tente fazer como exercício
antes:
Function ValidarCPF(CPF As String) As Boolean
If IsNumeric(CPF) Then
If Len(CPF) <
11 Then
CPF =
String(11 - Len(CPF), "0") & CPF
End If
If Right$(CPF, 2)
= CalcularDAC(CPF) Then
ValidarCPF =
True
Else
ValidarCPF =
False
End If
Else
ValidarCPF = False
End If
End Function
Private Function CalcularDAC(CPF As String) As String
Dim Soma As Integer
Dim Resto As Integer
Dim DAC1 As Integer
Dim DAC2 As Integer
Soma = 10 *
CInt(Left(CPF, 1)) + 9 * CInt(Mid(CPF, 2, 1)) + 8 * _
CInt(Mid(CPF, 3, 1)) + 7 *
CInt(Mid(CPF, 4, 1)) + 6 * _
CInt(Mid(CPF, 5, 1)) + 5 * CInt(Mid(CPF, 6, 1)) + 4 * _
CInt(Mid(CPF,
7, 1)) + 3 * CInt(Mid(CPF, 8, 1)) + 2 * _
CInt(Mid(CPF, 9, 1))
Resto = Soma Mod 11
If Resto = 0 Or Resto
= 1 Then
DAC1 = 0
Else
DAC1 = 11 - Resto
End If
Soma = 11 *
CInt(Left(CPF, 1)) + 10 * CInt(Mid(CPF, 2, 1)) + 9 * _
CInt(Mid(CPF, 3, 1)) + 8 *
CInt(Mid(CPF, 4, 1)) + 7 * _
CInt(Mid(CPF, 5, 1)) + 6 * CInt(Mid(CPF, 6, 1)) + 5 * _
CInt(Mid(CPF, 7, 1)) + 4 * CInt(Mid(CPF, 8, 1)) + 3 * _
CInt(Mid(CPF, 9, 1)) + 2 * DAC1
Resto = Soma Mod 11
If Resto = 0 Or Resto
= 1 Then
DAC2 = 0
Else
DAC2 = 11 - Resto
End If
CalcularDAC =
CStr(DAC1) & CStr(DAC2)
End Function
Private Sub OrdenarTabela()
Dim UltimaLinha As Long
Dim Intervalo As Range
PlFuncionarios.Activate
UltimaLinha = PlFuncionarios.Cells.Find("*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Set Intervalo = PlFuncionarios.Range(Cells(1, 1), Cells(UltimaLinha, 7))
With PlFuncionarios.Sort
Dim Intervalo As Range
PlFuncionarios.Activate
UltimaLinha = PlFuncionarios.Cells.Find("*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Set Intervalo = PlFuncionarios.Range(Cells(1, 1), Cells(UltimaLinha, 7))
With PlFuncionarios.Sort
.SortFields.Clear
.SortFields.Add
Range("A1")
.Header = xlYes
.SetRange
Intervalo
.Apply
End With
End Sub
Até aqui vimos apenas a classe, que aparentemente está pronta - se você exercitou e fez as rotinas mencionadas acima. A parte do formulário
segue em um artigo à parte para não deixar este tão extenso e para facilitar a
consulta quando necessário (se quiser informação sobre classe vem aqui, se
quiser sobre formulário vai no outro).
Para dúvidas sobre o artigo, comentários ou sugestões, utilize os comentários abaixo. Até o próximo artigo!
Pedro Martins
Pós-graduando em Business Intelligence e Big Data pela Faculdade Impacta de Tecnologia. Formado em Tecnologia em Eletrônica Digital com Ênfase em Microprocessadores
Meus muitos parabéns ao prof Alessandro trovato sensacional seu trabalho ,sua dedicação de esta compartilhando seus conhecimentos,sao poucas pessoas q pensam dá mesma forma q vc ...Ameie estou amando seu trabalho seus cursos no geral .Muitos sucesso Deus te abençoe grandimente .Você não e dez mas é mais de mil explica tudo muito bem explicado e uma forma muito fácil de aprender.
ResponderExcluirBoa noite Debora! Muito obrigado pela sua mensagem e pela gentileza em dedicar um tempo para redigir essa mensagem. Fico muito grato pelos elogios! É com grande satisfação que produzo os conteúdos para distribuição à todos que tenham interesse em aprender e que não tenham condições de investir em um curso presencial. Desejo a você muito sucesso, bons estudos e espero que ainda encontre muito material de estudo em meu blog e no canal no Youtube!
ExcluirUma função mais "enxuta" de validar CPF...
ResponderExcluirPublic Function fxValidarCPF(ByVal CPF As String) As Boolean
Dim arrDigits(1 To 11) As Byte, digtVer1 As Byte, digtVer2 As Byte, i As Byte
Dim sum1 As Integer, sum2 As Integer
Dim tmp As String
For i = 1 To Len(CPF)
If Mid(CPF, i, 1) Like "[0-9]" Then tmp = tmp & Mid(CPF, i, 1)
Next i
If Len(CPF) > 11 Then Exit Function
For i = 1 To (11 - Len(CPF))
CPF = "0" & CPF
Next i
For i = 0 To 9
If CPF = Application.WorksheetFunction.Rept(i, 11) Then Exit Function
Next i
For i = LBound(arrDigits) To UBound(arrDigits)
arrDigits(i) = CByte(Mid(CPF, i, 1))
Next i
For i = 1 To 10
If i < 10 Then sum1 = sum1 + (arrDigits(i) * i)
If i > 1 Then sum2 = sum2 + (arrDigits(i) * (i - 1))
Next i
If sum1 Mod 11 = 10 Then digtVer1 = 0 Else digtVer1 = sum1 Mod 11
If sum2 Mod 11 = 10 Then digtVer2 = 0 Else digtVer2 = sum2 Mod 11
If digtVer1 = arrDigits(10) And digtVer2 = arrDigits(11) Then fxValidarCPF = True
End Function