Tag Archives: UserForm

Excel VBA – Usando o controle TreeView

Tradução do artigo publicado em: http://www.excelguru.ca/node/85
Autor: Ken Puls

Introdução

Neste tutorial criaremos um UserForm que conterá um controle TreeView no Excel VBA. O objetivo é preencher o controle TreeView com dados que refletirão os nomes das planilhas da pasta de trabalho aberta e as células dentro destas que contém alguma fórmula.

Como este artigo envolve programação VBA e controles UserForm, sugerimos alguma familiaridade com estas duas tecnologias por parte do leitor para um melhor entendimento, além de alguma experiência com o ambiente VBE (Visual Basic Editor).

Para deixar o exemplo simples, os nomes dos controles foram mantidos como padrão, porém, sinta-se a vontade para renomeá-los a seu modo, de forma que facilite o acompanhamento do exemplo. Lembre-se apenas de refletir os nomes dos controles alterados nos devidos blocos de código para proporcionar o correto funcionamento do exemplo.

Criando a estrutura para o exemplo:

Siga os seguintes passos para criar nosso exemplo:

  1. Crie uma nova pasta de trabalho
  2. Adicione algumas fórmulas nas planilhas 1 e 2, não importa qual, mas precisa ser uma fórmula
  3. Entre no VBA (Alt+F11)
  4. Adicione um UserForm ao projeto
  5. Adiciona um novo módulo e adicione o seguinte código para chamar o UserForm:
    Sub ufLaunch()
         'Abre o userform
         UserForm1.Show
    End Sub

Adicionando o controle TreeView à barra de ferramentas:

  1. Clique com o botão direita na barra de ferramentas do VBA e selecione a opção “Adicionar Controles…”, conforme mostra a figura abaixo:
  2. Role a barra até encontrar o item “Microsoft Treeview Control, version x.x” O mais provável é que a versão mostrada seja a 6.0, mas não deve fazer diferença. Selecione o item e clique em OK.
  3. O controle será adicionado a barra de ferramentas conforme mostrado abaixo:
  4. NOTA: O controle TreeView permanecerá na sua barra de ferramentas até que este seja removido

Criando e configurando o userform:

  1. Adicione os seguinte controles no userform:
    1. TreeView
    2. Label
    3. CommandButton

    O userform deverá ser contruído para que fique parecido com o seguinte:

Adicionando o código que faz o trabalho:

As rotinas abaixo devem ser adicionadas ao UserForm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Private Sub UserForm_Initialize()
'Carrega os dados e configura o form
 
     'Congfigura os controles
     With Me
          .CommandButton1.Caption = "Close"
          .Label1 = vbNullString
          .TreeView1.LineStyle = tvwRootLines
     End With
 
     'Preenche o Treeview
     Call TreeView_Populate
End Sub
 
Private Sub TreeView_Populate()
'Preenche os dados do controle TreeView
 
     Dim ws As Worksheet
     Dim rngFormula As Range
     Dim rngFormulas As Range
 
     With Me.TreeView1.Nodes
          'Limpa os dados do controle
          .Clear
 
          For Each ws In ActiveWorkbook.Worksheets
               'Add worksheet nodes
               .Add Key:=ws.Name, Text:=ws.Name
 
               'Verifica de existe alguma fórmula na planilha
               On Error Resume Next
               Set rngFormulas = ws.Cells.SpecialCells(xlCellTypeFormulas)
               On Error GoTo 0
 
               'Adiciona as células que contém fórmulas
               If Not rngFormulas Is Nothing Then
                    For Each rngFormula In rngFormulas
                         .Add relative:=ws.Name, _
                              relationship:=tvwChild, _
                              Key:=ws.Name & "," & rngFormula.Address, _
                              Text:="Range " & rngFormula.Address
                    Next rngFormula
               End If
 
               'Zera o range para a próxima interação do laço
               Set rngFormulas = Nothing
          Next ws
     End With
End Sub
 
Private Sub Treeview1_NodeClick(ByVal Node As MSComctlLib.Node)
'Descreve o nó selecionado
 
     Me.Label1.Caption = Node.Key
End Sub
 
Private Sub CommandButton1_Click()
'Fecha o form
 
     Unload Me
End Sub

O resultado final:
Salve o projeto e retorne a interface do Excel. Pressione Alt+F8 para ativar a tela de macros e selecione a macro “ufLaunch” para execução. O userForm deverá ser mostrado. Como descrito anteriormente, foi solicitado que algumas fórmulas fossem adicionadas nas planilhas. A TreeView inicialmente lista as planilhas existentes e aquelas que possuem fórmulas possuem itens filhos que refletem quais células as possuem. A figura abaixo demonstra o resultado.

Explicando o código:

O userForm faz uso de quatro rotinas que são descritas abaixo:

Private Sub UserForm_Initialize()

Carrega as configurações necessárias para o funcionamento do userForm. É o local ideal para colocar todas as configurações necessárias dos controles, ou pelo menos aquelas que não podem ser feitas em modo de Desgin.Além disso, faz a chamada a rotina que preenche os dados da TreeView.

De especial, a propriedade LineStyle é setada para tvwRootLines. O padrão é tvwTreeLines, que não mostra os indicadores + e – para expansão dos itens, sendo possível apenas por duplo-clique. Por questões práticas, utilizar os sinalizadores + e – torna a tarefa de navegação mais fácil e intuitiva. Por isso a configuraremos assim.

Private Sub TreeView_Populate()

Preenche os dados da TreeView com os nomes das planilhas como nós principais e os nós filhos sendo o endereço das células que contém fórmulas.

O laço feito para varrer as planilhas e células com fórmulas é auto-explicativo e por isso não vamos entrar em detalhes deste ponto

Enquanto os nós são adicionados na TreeView, é preciso definir seus relacionamentos com os nós existentes, podendo ser pai ou filho. Neste caso, as planilhas serão os nós/pai das células que contém as fórmulas.

Para adicionar um nó no nível raiz, é preciso mencionar tanto a chave/nome do nó (Key) como o texto, dados solicitados como parâmetro no método Add. A chave precisa ser única para que o nó seja colocado no nível mais alto. O exemplo de código abaixo adiciona um novo nó com o nome de uma planilha:

.Add Key:=ws.Name, Text:=ws.Name

i.e. Me.TreeView1.Nodes.Add Key:=”Sheet1″, Text:=”Sheet1″

Toda vez em que for necessário adicionar um nó no nível mais alto, a sintaxe acima deve ser utilizada.

Para adicionar nós filho, o processo é um pouco diferente. O método utilizado ainda é o Add, bem como é preciso informar uma chave única para o nó adicionado. Porém é preciso informar o “grau” de parentesco do nó adicionado e quem é o nó relacionado. No caso de um nó filho, o parâmetro a ser informado deve ter o valor tvwChild). O código abaixo adiciona um nó filho ao TreeView:

.Add relative:=ws.Name, relationship:=tvwChild, _
Key:=ws.Name & "," & rngFormula.Address, Text:="Range " & rngFormula.Address

i.e. Me.TreeView1.Nodes.Add relative:= “Sheet1″, relationship:=tvwChild Key:=”Sheet1,$C$8″, Text:=”Rage $C$8”

Como dito, para “conectar” um nó filho ao nó pai, o parâmetro “relative” deve ser informado, e este deve ser o “key” do nó pai. De qualquer forma, o novo nó também precisa ter seu key e este deve ser único.

Private Sub Treeview1_NodeClick(ByVal Node As MSComctlLib.Node)

Este é o evento disparado quando um nó do TreeView é clicado. A rotina captura o nó que foi clicado e escreve sua identificação no label1. É interessante notar que quando um nó filho é clicado, ele leva também a identificação do nó pai, separado por vírgula. Apesar de parecer um detalhe, este formado auxiliaria o acesso a um determinado nó pelo seu valor usando esta string através da função Split(), que retornaria um array com os valores respectivos.

Private Sub CommandButton1_Click()
Simplesmente fecha o userForm.

Conclusão:

Uma vez seguido o tutorial, percebe-se que o funcionamento do controle TreeView é bastante intuitivo. O exemplo foi aplicado ao Excel, fazendo uso de seus objetos, mas poderia facilmente ser aplicado ao Access, Word ou qualquer aplicação que faça uso do VBA. Na verdade, o controle é disponibilizado no Windows, de forma que qualquer aplicativo que ofereça interface de programação, como Delphi o VB tradicional.

Adicionando imagens ao TreeView:

É comum ver o controle TreeView ser utlizado em várias operações do Microsoft Windows, a começar pelo próprio Windows Explorer. No caso deste último, pode-se ver que há imagens associadas a cada nó, como pode ser visto na figura abaixo:

Como na figura acima, é possível associar imagens aos nós do TreeView. Um exemplo de como adicionar imagens ao TreeView pode ser visto neste link.

Usando a TreeView para navegar pelos Ranges:

A rotina abaixo adiciona a funcionalidade de navegação ao userForm TreeView. Adicione um botão ao userForm e adicione o código abaixo. Isso fará com que a aplicação navegue até o nó selecionado e feche o form. Se for uma planilha, nevegará até a mesma, se for uma célula, o mesmo será feito:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Private Sub CommandButton2_Click()
'Purpose: Go to the selected node
 
     Dim sNodes() As String
 
     'Verifica se existe algum nó selecionado
     If Me.Label1.Caption = vbNullString Then
          MsgBox "You have not selected anything!" & vbNewLine & _
                 "Please select something and try again.", vbCritical + vbOKOnly, _
                 "Nothing selected"
          Exit Sub
     End If
 
     'faz o split do conteúdo do Label
     sNodes = Split(Me.Label1, ",")
 
     With Worksheets(sNodes(0))
          'Ativa a planilha
          .Activate
 
          'Ativa o range
          If UBound(sNodes) + 1 > 1 Then
               'Ativa o range selecionado
               .Range(sNodes(1)).Activate
          Else
               'Quando da seleção de uma planilha, ativa a célula A1
               .Range("A1").Activate
          End If
     End With
 
     'Fecha o userform
     Unload Me
 
End Sub

VBA – Preenchendo um Listbox com valores únicos de uma lista

Fonte: http://www.exceltip.com

A macro abaixo preenche um ListBox (que também poderia ser um ComboBox) em um UserForm com os valores únicos de um range. No VBA, crie um UserForm, insira um ListBox e coloque o código abaixo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Private Sub UserForm_Initialize()
    Dim MyUniqueList As Variant, i As Long
    With Me.ListBox1
        .Clear    ' limpa o conteúdo do listbox
        MyUniqueList = UniqueItemList(Range("A1:A30"), True)
        For i = 1 To UBound(MyUniqueList)
            .AddItem MyUniqueList(i)
        Next i
        .ListIndex = 0    ' seleciona o primeiro item
    End With
End Sub
 
Private Function UniqueItemList(InputRange As Range, _
                                HorizontalList As Boolean) As Variant
    Dim cl As Range, cUnique As New Collection, i As Long, uList() As Variant
    Application.Volatile
    On Error Resume Next
    For Each cl In InputRange
        If cl.Formula <> "" Then
            cUnique.Add cl.Value, CStr(cl.Value)
        End If
    Next cl
    UniqueItemList = ""
    If cUnique.Count > 0 Then
        ReDim uList(1 To cUnique.Count)
        For i = 1 To cUnique.Count
            uList(i) = cUnique(i)
        Next i
        UniqueItemList = uList
        If Not HorizontalList Then
            UniqueItemList = _
            Application.WorksheetFunction.Transpose(UniqueItemList)
        End If
    End If
    On Error GoTo 0
End Function

Neste caso, estou supondo que existe uma lista de valores entre as células A1 e A30 da planilha ativa. A função pode facilmente ser transformada para retornar um array com valores únicos.

Disabling close button in UserForm VBA

Alright, you’ve built an amazing Userform for your user, which works perfectly on every single function. A master piece of software! But, something remains out of your control, a simple click and everything is lost due to… the infamous close button! Ok, you know there is an easy and clever way to avoid this situation, handling the Query_Close event and the Cancel parameter.

So far so good, but… why do you and your user need this button there? Until this question remains unsolved, let’s look to another way to set our lives free about this problem.

Uma necessidade comum em alguns sistemas VBA é evitar que o usuário feche o form involuntariamente. A forma mais comum é captura o evento Query_Close e alterar o valor do parâmetro Cancel.

Porém algumas vezes é necessário que o botão de fechamento nem apareça para alterar. Para isso, basta colocar o código abaixo em um UserForm VBA. Os eventos Initialize e Query_Close já criados tratam de remover o botão do UserForm.

Private Declare Function FindWindowA Lib "USER32" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLongA Lib "USER32" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLongA Lib "USER32" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
 
Private Sub UserForm_Initialize()
    Dim hwnd As Long
    hwnd = FindWindowA(vbNullString, Me.Caption)
    SetWindowLongA hwnd, -16, GetWindowLongA(hwnd, -16) And &HFFF7FFFF
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    Dim hwnd As Long
    hwnd = FindWindowA(vbNullString, Me.Caption)
    SetWindowLongA hwnd, -16, GetWindowLongA(hwnd, -16) Or &H80000
End Sub

É bom atentar que desaparecer com o botão não desativa o uso do atalho Alt+F4 que fecha a janela.

Bom proveito!

Tomás Vásquez
http://www.tomasvasquez.com.br