Criar um formulário de utilizador

Os formulários de utilizador são caixas de diálogo personalizadas utilizadas para recolher dados de entrada do utilizador no início de uma macro. O UserForm Builder incluído no Code VBA cria automaticamente UserForms. Os formulários de utilizador gerados têm um código de validação de entrada adicionado para garantir que todas as entradas necessárias são do tipo correto. Isto tornará a macro mais fiável. O facto de os controlos e o código de validação serem gerados poupa muito tempo que teria de despender para criar o formulário de utilizador manualmente. O UserForm pode ser alterado para tornar o design visualmente mais atrativo, movendo e redimensionando os controlos. Além disso, é possível alargar o código vba do formulário de utilizador para satisfazer requisitos adicionais.

Utilizar o UserForm Builder

O UserForm Builder é iniciado a partir do menu Código VBA:

Nome do UserForm: Controlos Nome Tipo de dados Tipo de controlo Necessário Selecionar nomes... Criar criador de formulários de utilizador

No UserForm Builder, especificará

  • UserForm Name (Nome do formulário do utilizador): dê um nome ao formulário que seja curto para os dados que serão introduzidos. No exemplo, o nome é "Order" (encomenda)
  • A grelha de Controlos é a área em que especifica os controlos que pretende incluir no UserForm que está a criar. Cada controlo é especificado numa linha separada com propriedades:
    • Nome: será utilizado como etiqueta para o controlo de entrada.
    • Tipo de dados: se o utilizador introduzir um valor de tipo incorreto no controlo, ao premir o botão OK, o utilizador recebe uma mensagem de que o valor desse campo está incorreto e volta a colocar o cursor nesse campo para que o utilizador o corrija.
    • Tipo de controlo: permite-lhe especificar o tipo de controlo preferido. Um booleano (sim/não) pode ser representado de uma forma fácil de utilizar através de um controlo
    • Obrigatório: se o utilizador não introduzir um valor no controlo, ao premir o botão OK o utilizador recebe uma mensagem a informar que falta um valor para esse campo e coloca o cursor nesse campo.
  • Selecionar nomes...: abre uma janela caixa de entrada de seleção de intervalo para lhe permitir selecionar um ou mais nomes da sua folha de Excel. Isto é útil se a caixa de diálogo for criada para preencher um formulário ou tabela Excel existente.
  • Create: cria o UserForm - imagem abaixo - e insere o código para iniciar o userform.
criador de formulários de utilizador

Código VBA do UserForm

Para além do objeto UserForm, o construtor também adiciona três partes de código:

Código para abrir / mostrar o formulário do utilizador

Supondo que o cursor estava dentro do procedimento Sub Demo, depois de premir OK no UserForm Builder, o procedimento teria o seguinte aspeto


Sub Demo()
    Dim udtOrder As Order
    With udtOrder
        .Client = ""
        .EntryDate = Date
        .Product = ""
        .Attention = True
    End With
    ufmOrder.FillList "cboProduct", Array("v1", "v2", "v3")
    ufmOrder.SetValues udtOrder
    ufmOrder.Show
    If Not ufmOrder.IsCancelled Then
        ufmOrder.GetValues udtOrder
        ''continue process after OK here
        With udtOrder

        End With
    End If
    Unload ufmOrder
End Sub

Para obter uma interface limpa, é utilizado um Tipo Definido pelo Utilizador que inclui os controlos de dados com as respectivas definições de tipo no formulário do utilizador. Pode alterar o código entre o primeiro With e End With para alterar os valores iniciais ou predefinidos. A passagem dos valores para o formulário é efectuada no método SetValues

O procedimento FillList é utilizado para passar uma matriz de valores para a caixa de listagem Product. Os valores da matriz ("v1",...) são apenas um exemplo que terá de adaptar.

O método Show é a forma padrão de abrir o UserForm.

O utilizador pode premir OK ou Cancelar a caixa de diálogo. Isto é determinado através do teste da propriedade .IsCancelled.

Se o utilizador tiver premido OK, a macro continuará a utilizar os dados que o utilizador forneceu no formulário do utilizador. Para o efeito, adicionará o seu próprio código de processo entre o segundo With e End With.

Tipo definido pelo utilizador para uma interface limpa

O Tipo Definido pelo Utilizador gerado fornece uma interface limpa entre a sua macro e o UserForm. Se o procedimento a partir do qual está a chamar o formulário do utilizador estiver num módulo padrão, o tipo definido pelo utilizador será aí colocado. Caso contrário, será colocado num módulo chamado 'modTypes'. No exemplo, a declaração de tipo tem o seguinte aspeto:


Public Type Order
    Client As String
    EntryDate As Date
    Product As String
    Attention As Boolean
End Type
Nota - Criador de tipos definidos pelo utilizador
Semelhante ao construtor explicado aqui O código VBA também inclui um construtor de tipos definidos pelo utilizador:

Validação do UserForm e outro código

No interior do UserForm gerado, existe ainda uma grande quantidade de código que é utilizado para:

Manuseamento de OK e Cancelar

O código abaixo mostra o tratamento dos botões Ok e Cancelar. O botão Fechar também é tratado adequadamente como Cancelar sem necessidade de código adicional. A variável pública IsCancelled é utilizada para comunicar à macro de chamada se o utilizador premiu Ok ou Cancelar.


Public IsCancelled As Boolean

Private Sub UserForm_Initialize()
    IsCancelled = True
End Sub

Private Sub btnCancel_Click()
    Me.Hide
End Sub

Private Sub btnOk_Click()
    If IsInputOk Then
        IsCancelled = False
        Me.Hide
    End If
End Sub

Transmissão de dados para o formulário do utilizador

Os dados são transmitidos e obtidos a partir do formulário do utilizador através de SetValues e GetValues, respetivamente. Ambos utilizam a variável Tipo definido pelo utilizador.


Public Sub SetValues(udtOrder As Order)
    With udtOrder
        SetValue Me.txtClient, .Client
        SetValue Me.txtEntryDate, .EntryDate
        SetValue Me.cboProduct, .Product
        SetValue Me.cbxAttention, .Attention
    End With
End Sub

Public Sub GetValues(ByRef udtOrder As Order)
    With udtOrder
        .Client = GetValue(Me.txtClient, TypeName(.Client))
        .EntryDate = GetValue(Me.txtEntryDate, TypeName(.EntryDate))
        .Product = GetValue(Me.cboProduct, TypeName(.Product))
        .Attention = GetValue(Me.cbxAttention, TypeName(.Attention))
    End With
End Sub

SetValues e GetValues são implementados utilizando os procedimentos abaixo indicados:


Private Sub SetValue(ctl As MSForms.Control, value As Variant)
    On Error GoTo HandleError
    ctl.value = value
HandleExit:
    Exit Sub
HandleError:
    Resume HandleExit
End Sub

Private Function GetValue(ctl As MSForms.Control, strTypeName As String) As Variant
    On Error GoTo HandleError
    Dim value As Variant
    value = ctl.value
    If IsNull(value) And strTypeName <> "Variant" Then
        Select Case strTypeName
        Case "String"
            value = ""
        Case Else
            value = 0
        End Select
    End If
HandleExit:
    GetValue = value
    Exit Function
HandleError:
    Resume HandleExit
End Function

Validar a entrada do formulário do utilizador

Quando o utilizador prime OK, os dados do UserForm são validados pela função IsInputOk. Esta verifica, para cada controlo de entrada IsInputControl, se tem um valor HasValue, caso seja necessário IsRequired. Em seguida, verifica se o valor é do tipo correto IsCorrectType - tal como foi especificado no UserForm Builder. A não aprovação em qualquer um dos testes resulta numa mensagem para o utilizador e na colocação do foco no controlo cujo valor falhou ctl.SetFocus.


Private Function IsInputOk() As Boolean
    Dim ctl As MSForms.Control
    Dim strMessage As String
    IsInputOk = False
    For Each ctl In Me.Controls
        If IsInputControl(ctl) Then
            If IsRequired(ctl) Then
                If Not HasValue(ctl) Then
                    strMessage = ControlName(ctl) & " must have value"
                End If
            End If
            If Not IsCorrectType(ctl) Then
                strMessage = ControlName(ctl) & " is not correct"
            End If
        End If
        If Len(strMessage) > 0 Then
            ctl.SetFocus
            GoTo HandleMessage
        End If
    Next
    IsInputOk = True
HandleExit:
    Exit Function
HandleMessage:
    MsgBox strMessage
    GoTo HandleExit
End Function

Note-se que IsCorrectType utiliza a função ControlDataType que devolve simplesmente o tipo para o controlo dado utilizando as instruções Select Case inseridas pelo UserForm Builder. Uma abordagem semelhante, mas mais simples, é seguida para IsRequired.


Private Function IsCorrectType(ctl As MSForms.Control) As Boolean
    Dim strControlDataType As String, strMessage As String
    Dim dummy As Variant
    strControlDataType = ControlDataType(ctl)
    On Error GoTo HandleError
    Select Case strControlDataType
    Case "Boolean"
        dummy = CBool(GetValue(ctl, strControlDataType))
    Case "Byte"
        dummy = CByte(GetValue(ctl, strControlDataType))
    Case "Currency"
        dummy = CCur(GetValue(ctl, strControlDataType))
    Case "Date"
        dummy = CDate(GetValue(ctl, strControlDataType))
    Case "Double"
        dummy = CDbl(GetValue(ctl, strControlDataType))
    Case "Decimal"
        dummy = CDec(GetValue(ctl, strControlDataType))
    Case "Integer"
        dummy = CInt(GetValue(ctl, strControlDataType))
    Case "Long"
        dummy = CLng(GetValue(ctl, strControlDataType))
    Case "Single"
        dummy = CSng(GetValue(ctl, strControlDataType))
    Case "String"
        dummy = CStr(GetValue(ctl, strControlDataType))
    Case "Variant"
        dummy = CVar(GetValue(ctl, strControlDataType))
    End Select
    IsCorrectType = True
HandleExit:
    Exit Function
HandleError:
    IsCorrectType = False
Resume HandleExit
End Function

Private Function ControlDataType(ctl As MSForms.Control) As String
    Select Case ctl.Name
    Case "txtClient": ControlDataType = "String"
    Case "txtEntryDate": ControlDataType = "Date"
    Case "cboProduct": ControlDataType = "String"
    Case "cbxAttention": ControlDataType = "Boolean"
    End Select
End Function

Private Function IsRequired(ctl As MSForms.Control) As Boolean
    Select Case ctl.Name
    Case "txtClient", "txtEntryDate", "cboProduct", "cbxAttention"
        IsRequired = True
    Case Else
        IsRequired = False
    End Select
End Function