VBA – Criando ComboBox Encadeados

Outra dúvida muito comum na comunidade VBA. ComboBox Encadeados quer dizer que em dois ou mais ComboBoxes, ao atualizar os dados de um ComboBox, os outros são afetados. Isso é útil quando os dados entre eles estão relacionados, o que quase sempre é verdade.

No exemplo deste post, a relação dos dados será baseada na tradicional Produto e Categoria. A pasta de trabalho deve estar disposta em duas planilhas da seguinte forma:

Planilha de Categorias

Planilha de Produtos

É preciso que os dados da coluna NomeDaCategoria na planilha de Produtos estejam compatíveis com os da planlha Categorias, ou o filtro não funcionará.

Após isso, crie um UserForm com a seguinte “cara”:

Estrutura do Formulário ComboBox Encadeados

Para que o código funcione sem precisar alterar nada, nomeie os campos da seguinte forma:

LabelCategorias
ComboBoxCategorias
LabelProdutos
ComboBoxProdutos

Em seguida, adicione o seguinte código o formulário:

Private Sub ComboBoxCategorias_Change()
    Call CarregaProdutos(Me.ComboBoxCategorias.List(Me.ComboBoxCategorias.ListIndex))
End Sub
 
Private Sub UserForm_Initialize()
    Call CarregaCategorias
End Sub
 
Private Sub CarregaCategorias()
    Dim linha As Integer, coluna As Integer
    linha = 2
    coluna = 1
    Me.ComboBoxCategorias.Clear
    With Sheets("Categorias")
        Do While Not IsEmpty(.Cells(linha, coluna))
            Me.ComboBoxCategorias.AddItem .Cells(linha, coluna).Value
            linha = linha + 1
        Loop
    End With
End Sub
 
Private Sub CarregaProdutos(ByVal Categoria As String)
    Dim linha As Integer, colunaProduto As Integer, colunaCategoria As Integer
    linha = 2
    colunaProduto = 1
    colunaCategoria = 2
    Me.ComboBoxProdutos.Clear
    With Sheets("Produtos")
        Do While Not IsEmpty(.Cells(linha, colunaProduto))
            If .Cells(linha, colunaCategoria).Value = Categoria Then
                Me.ComboBoxProdutos.AddItem .Cells(linha, colunaProduto).Value
            End If
            linha = linha + 1
        Loop
    End With
End Sub

Execute o formulário e selecione uma categoria no primeiro ComboBox. Veja que no ComboBox de produtos são mostrados apenas aqueles que pertecem à categoria selecionada:

ComboBox Encadeados em Funcionamento

Obviamente existem várias formas de fazer o mesmo trabalho. Invente a sua, ou, faça o download do exemplo no link abaixo:
😉

http://www.tomasvasquez.com.br/forum/download/file.php?id=2150

Bom proveito!

VBA – A diferenças entre InputBox e Application.InputBox

Introdução

O VBA disponibiliza além da tradicional função InputBox, um método de nome Application.InputBox. A principal questão é, já que existem duas, qual se deve utilizar?

A principal diferenção entre elas é que, a função InputBox é uma maneira simples de capturar textos introduzidos pelo usuário. Já o método Application.InputBox é capaz de verificar automaticamente o tipo de dados que deve ser inserido na caixa de texto do caixa de diálogo mostrada.

Estrutura do Application.InputBox

Application.InputBox(Prompt, Title, Default, Left, Top, HelpFile, HelpContextId, Type)

Prompt

String necessário. A mensagem a ser exibida na caixa de diálogo. Pode ser uma seqüência de caracteres, um número, uma data ou um valor Boolean (o Microsoft Excel converte o valor automaticamente em um String antes de exibi-lo).

Title

Variant opcional. O título da caixa de entrada. Se esse argumento for omitido, o título padrão será “Entrada”.

Default

Variant opcional. Especifica um valor que aparecerá na caixa de texto quando a caixa de diálogo for inicialmente exibida. Se esse argumento for omitido, a caixa de texto será deixada vazia. Esse valor pode ser um objeto Range.

Left

Variant opcional. Especifica uma posição x para a caixa de diálogo em relação ao canto superior esquerdo da tela, em pontos.

Top

Variant opcional. Especifica uma posição y para a caixa de diálogo em relação ao canto superior esquerdo da tela, em pontos.

Help File

Variant opcional. O nome do arquivo de ajuda para essa caixa de entrada. Se os argumentos HelpFile e HelpContextID estiverem presentes, um botão Ajuda aparecerá na caixa de diálogo.

Help ContextId

Variant opcional. O número de identificação do contexto do tópico da Ajuda em HelpFile.

Type

Variant opcional. Especifica o tipo de dados retornado. Se esse argumento for omitido, a caixa de diálogo retornará texto. Pode ser um dos valores abaixo ou a soma deles.

clip_a04[1]

Exemplo

Note que a única diferença desta para a função InputBox, é a existência do último parâmetro, o Type. É este que permite fazer validações de tipos simples do VBA para retorno da função. Veja o código de exemplo:

1
2
3
4
5
Public Sub TesteAppInputBox()
Dim num As Long
num = Application.InputBox("Digite um valor numérico", "Application.InputBox", "Valor numérico", , , , , 1)
MsgBox "O valor digitado foi: " & num
End Sub

No código, os parâmetros Left, Top, HelpFile e HelpContextId foram omitidos por não serem necessários para este exemplo. O importante neste código é o parâmetro Type que foi informado. Conforme a tabela, o valor 1 significa que o Application.InputBox espera que um valor numérico seja inserido pelo usuário. Execute a função e veja o resultado:

clip_d76[1]

Informe um valor de texto qualquer na caixa de entrada e clique em OK para ver o resultado:

clip_28a[1]

Veja que não necessário nenhuma verificação via código VBA. O Applcation.InputBox somente precisa da informação sobre o tipo de dado que deve ser esperado pelo usuário. Informe um valor numérico qualquer e clique novamente em OK:

clip_0e6[1]

Você pode usar a soma dos valores permitidos para Type. Por exemplo, para uma caixa de entrada que possa aceitar tanto texto como números, defina o parâmtro Type como 1 + 2.

Comentários

Use InputBox para exibir uma caixa de diálogo simples para que você possa inserir informações a serem usadas em uma macro. A caixa de diálogo tem um botão OK e um botão Cancelar. Quando você escolhe o botão OK, InputBox retorna o valor inserido na caixa de diálogo. Quando você clica no botão Cancelar, InputBox retorna False.

Quando Type é 0, InputBox retorna a fórmula na forma de texto — por exemplo, “=2*PI()/360”. Se houver quaisquer referências na fórmula, elas serão retornadas como referências de estilo A1. (Use ConvertFormula para fazer a conversão entre estilos de referências).

Quando Type é 8, InputBox retorna um objeto Range. Você precisa usar a instrução Set para atribuir o resultado a um objeto Range, como mostrado no exemplo seguinte.

1
Set meuRange = Application.InputBox(prompt := "Exemplo", type := 8)

Se você não usar a instrução Set, a variável será definida com o valor no intervalo em vez do próprio objeto Range.

Se você usar o método InputBox para pedir uma fórmula ao usuário, você terá que usar a propriedade FormulaLocal para atribuir a fórmula a um objeto Range. A fórmula de entrada deverá estar no idioma do usuário.

O método InputBox difere da função InputBox porque permite validação seletiva da entrada do usuário e aceita objetos, valores de erro e fórmulas do Microsoft Excel. Observe que Application.InputBox chama o método InputBox e InputBox sem qualificador de objeto chama a função InputBox.

VBA – Utilizando InputBox com Máscara (senha)

InputBox com Senha
InputBox com Senha

Tirado o blog do Luiz Cláudio que tirou do excelfiles.com. Aí vai mais uma referência.

O InputBox é função extremamente prática para coletar dados de maneira rápida ao usuário. Porém, seria muito bacana se fosse possível estender algumas de suas possbilidades. Uma delas seria se, por exemplo, fosse possível adicionar uma máscara de forma que o usuário pudesse digitar uma senha no InputBox.

Pois bem. A macro abaixo faz exatamente isso:

'////////////////////////////////////////////////////////////////////
'Password masked inputbox
'Allows you to hide characters entered in a VBA Inputbox.
'
'Code written by Daniel Klann
'http://www.danielklann.com/
'March 2003
 
'// Kindly permitted to be amended
'// Amended by Ivan F Moala
'// http://www.xcelfiles.com
'// April 2003
'// Works for Xl2000+ due the AddressOf Operator
'////////////////////////////////////////////////////////////////////
 
'API functions to be used
Private Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, _
                                                      ByVal ncode As Long, ByVal wParam As Long, lParam As Any) As Long
 
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" ( _
                                         ByVal lpModuleName As String) As Long
 
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _
                                          ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) _
                                          As Long
 
Private Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
 
Private Declare Function SendDlgItemMessage Lib "user32" Alias "SendDlgItemMessageA" ( _
                                            ByVal hDlg As Long, ByVal nIDDlgItem As Long, ByVal wMsg As Long, ByVal wParam As Long, _
                                            ByVal lParam As Long) As Long
 
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, _
                                                                          ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
 
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
 
'Constants to be used in our API functions
Private Const EM_SETPASSWORDCHAR = &HCC
Private Const WH_CBT = 5
Private Const HCBT_ACTIVATE = 5
Private Const HC_ACTION = 0
 
Private hHook As Long
 
Public Function NewProc(ByVal lngCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
 
    Dim RetVal
    Dim strClassName As String, lngBuffer As Long
 
    If lngCode < HC_ACTION Then
        NewProc = CallNextHookEx(hHook, lngCode, wParam, lParam)
        Exit Function
    End If
 
    strClassName = String$(256, " ")
    lngBuffer = 255
 
    If lngCode = HCBT_ACTIVATE Then    'A window has been activated
        RetVal = GetClassName(wParam, strClassName, lngBuffer)
        If Left$(strClassName, RetVal) = "#32770" Then    'Class name of the Inputbox
            'This changes the edit control so that it display the password character *.
            'You can change the Asc("*") as you please.
            SendDlgItemMessage wParam, &H1324, EM_SETPASSWORDCHAR, Asc("*"), &H0
        End If
    End If
 
    'This line will ensure that any other hooks that may be in place are
    'called correctly.
    CallNextHookEx hHook, lngCode, wParam, lParam
 
End Function
 
'// Make it public = avail to ALL Modules
'// Lets simulate the VBA Input Function
Public Function InputBoxDK(Prompt As String, Optional Title As String, Optional Default As String, _
                           Optional Xpos As Long, Optional Ypos As Long, Optional Helpfile As String, _
                           Optional Context As Long) As String
 
    Dim lngModHwnd As Long, lngThreadID As Long
 
    '// Lets handle any Errors JIC! due to HookProc> App hang!
    On Error GoTo ExitProperly
    lngThreadID = GetCurrentThreadId
    lngModHwnd = GetModuleHandle(vbNullString)
 
    hHook = SetWindowsHookEx(WH_CBT, AddressOf NewProc, lngModHwnd, lngThreadID)
    If Xpos Then
        InputBoxDK = InputBox(Prompt, Title, Default, Xpos, Ypos, Helpfile, Context)
    Else
        InputBoxDK = InputBox(Prompt, Title, Default, , , Helpfile, Context)
    End If
 
ExitProperly:
    UnhookWindowsHookEx hHook
 
End Function

Para fazê-la funcionar, adicione o código acima a um módulo e use a seguinte chamanda:

1
2
3
Sub Teste()
    MsgBox InputBoxDK("Digite sua senha", "Atenção", "123456")
End Sub

Funciona muito bem. Agradecimentos ao autor da macro, o MVP inglês Daniel Klann.

C# – Obtendo todos os IDs de um List Type com o método List.ConvertAll

O LINQ, juntamente com as Lambda Expressions resolveram problemas que em versões anteriores do .NET Framework eram feitos através de Loops ou coisa parecida.

Um exemplo típico é quando desejamos obter um lista do IDs de uma lista tipada de objetos, por exemplo, Cliente ou Produto. Não tem jeito. É preciso um Loop for/while/foreach para capturar determinada lista.

Porém, a partir do .NET Framework 2.0 e o lançamento do Generics, trabalhos em listas passaram a ser bem mais simples. Neste caso, que faz a proeza é o método List.ConvertAll. O que ele faz é converter uma Lista (List) de um determinado tipo e um outra Lista cujo o tipo é informado.

Um exemplo de seu uso (da própria Microsoft) pode ser visto abaixo:

using System;
using System.Drawing;
using System.Collections.Generic;
 
public class Example
{
    public static void Main()
    {
        List
 lpf = new List
();
 
        lpf.Add(new PointF(27.8F, 32.62F));
        lpf.Add(new PointF(99.3F, 147.273F));
        lpf.Add(new PointF(7.5F, 1412.2F));
 
        Console.WriteLine();
        foreach( PointF p in lpf )
        {
            Console.WriteLine(p);
        }
 
        List
 lp = lpf.ConvertAll(
            new Converter
(PointFToPoint));
 
        Console.WriteLine();
        foreach( Point p in lp )
        {
            Console.WriteLine(p);
        }
    }
 
    public static Point PointFToPoint(PointF pf)
    {
        return new Point(((int) pf.X), ((int) pf.Y));
    }
}
 
/* O código de exemplo produz a seguinte saída:
 
{X=27.8, Y=32.62}
{X=99.3, Y=147.273}
{X=7.5, Y=1412.2}
 
{X=27,Y=32}
{X=99,Y=147}
{X=7,Y=1412}
 */

Porém, é possível dar exemplo “mais real”. Imagine que você tenha um classe Usuário e que ela tenha seus atributos tradicionais (ID, Nome, Idade, etc…). O código abaixo

// declara uma lista tipada da classe User
List Users = new List();
// adiciona alguns registros
Users.Add(new User(1, "User 1"));
Users.Add(new User(2, "User 2"));
Users.Add(new User(3, "User 3"));
// coleta uma lista tipada de int contendo os ids dos usuários com o método List.ConvertAll
List userIds = Users.ConvertAll(delegate(User user)
{
          return user.ID;
});
// imprime no Console
foreach(int id in userIds)
{
          Console.WriteLine(s);
}

Note que o uso do List.ConvertAll exige o uso de um delegate, que no caso, está utilizando um método anônimo (ver referências).

Uma mão da roda (para quem não tem LINQ)!

Referências:
http://msdn.microsoft.com/en-us/library/0yw3tz5k%28VS.80%29.aspx
http://msdn.microsoft.com/en-us/library/73fe8cwf%28VS.85%29.aspx
http://pontonetpt.com/blogs/israelaece/archive/2005/05/23/P4746.aspx