web-dev-qa-db-fra.com

Comment ajouter des en-têtes à une liste déroulante multicolonnes dans un formulaire utilisateur Excel à l'aide de VBA

Est-il possible de configurer les en-têtes dans une liste déroulante multicolonne sans utiliser une plage de feuille de calcul comme source?

Ce qui suit utilise un tableau de variantes affecté à la propriété list de la listbox, les en-têtes apparaissent vides.

Sub testMultiColumnLb()
    ReDim arr(1 To 3, 1 To 2)

    arr(1, 1) = "1"
    arr(1, 2) = "One"
    arr(2, 1) = "2"
    arr(2, 2) = "Two"
    arr(3, 1) = "3"
    arr(3, 2) = "Three"


    With ufTestUserForm.lbTest
        .Clear
        .ColumnCount = 2
        .List = arr
    End With

    ufTestUserForm.Show 1
End Sub
10
vzczc

Non, je crée des étiquettes au-dessus de la zone de liste pour servir d'en-têtes. Vous pensez peut-être qu'il est très pénible de changer d'étiquette à chaque changement de liste. Vous auriez raison - c'est pénible. C'est difficile à mettre en place la première fois et encore moins de changements. Mais je n'ai pas trouvé de meilleur moyen.

17
Dick Kusleika

Je regardais ce problème tout à l'heure et j'ai trouvé cette solution. Si votre RowSource pointe sur une plage de cellules, les en-têtes de colonne d'une liste déroulante à plusieurs colonnes sont extraits des cellules situées immédiatement au-dessus de RowSource.

À l'aide de l'exemple présenté ici, dans la zone de liste, les mots Symbole et Nom apparaissent sous la forme d'un en-tête. Lorsque j'ai changé le nom du mot dans la cellule AB1, puis que j'ai ouvert à nouveau le formulaire dans le VBE, les en-têtes de colonne ont changé.

Screenshot displaying a named range and the column headings outside the range.

L'exemple est tiré d'un livre de travail dans VBA For Modelers de S. Christian Albright, et j'essayais de comprendre comment il avait obtenu les en-têtes de colonne dans sa liste déroulante :)

6
Rick Henderson

Réponse simple: non.

Ce que j'ai fait par le passé est de charger les en-têtes dans la ligne 0, puis de définir le ListIndex sur 0 lors de l'affichage du formulaire. Cela met ensuite en surbrillance les "en-têtes" en bleu, donnant l'apparence d'un en-tête. Les boutons d'action du formulaire sont ignorés si le ListIndex reste à zéro, ces valeurs ne peuvent donc jamais être sélectionnées.

Bien sûr, dès qu'un autre élément de la liste est sélectionné, l'en-tête perd le focus, mais son travail est terminé.

Faire les choses de cette façon vous permet également d'avoir des en-têtes qui défilent horizontalement, ce qui est difficile/impossible à faire avec des étiquettes séparées qui flottent au-dessus de la zone de liste. Le revers de la médaille est que les en-têtes ne restent pas visibles si la zone de liste doit défiler verticalement.

Fondamentalement, c'est un compromis qui fonctionne dans les situations dans lesquelles j'ai été.

5
Lunatik

Il est très facile d'afficher les en-têtes en haut de la zone de liste multi-colonnes. Il suffit de changer la valeur de la propriété en "true" pour "columnheads" qui est false par défaut.

Après cela, il suffit de mentionner la plage de données dans la propriété "rowsource", à l'exclusion de l'en-tête de la plage de données et que l'en-tête doit être placé en première ligne du haut de la plage de données. L'en-tête sera alors sélectionné automatiquement et votre en-tête sera gelé.

Si vous supposez que les données se trouvent dans la plage "A1: H100" et que l'en-tête correspond à "A1: H1" (première ligne), votre plage de données doit être "A2: H100" et doit être mentionnée dans les propriétés "rowsource" et "columnheads "la valeur de perpétuité doit être vraie

Cordialement, Asif Hameed

4
Asif Hameed

Voici mon approche pour résoudre le problème:

Cette solution nécessite que vous ajoutiez un deuxième élément ListBox et le placiez au-dessus du premier. 

Comme ça:

 Add an additional ListBox

Ensuite, vous appelez la fonction CreateListBoxHeader pour corriger l'alignement et ajouter des éléments d'en-tête.

Résultat:

 Call the function CreateListBoxHeader

Code:

  Public Sub CreateListBoxHeader(body As MSForms.ListBox, header As MSForms.ListBox, arrHeaders)
            ' make column count match
            header.ColumnCount = body.ColumnCount
            header.ColumnWidths = body.ColumnWidths

        ' add header elements
        header.Clear
        header.AddItem
        Dim i As Integer
        For i = 0 To UBound(arrHeaders)
            header.List(0, i) = arrHeaders(i)
        Next i

        ' make it pretty
        body.ZOrder (1)
        header.ZOrder (0)
        header.SpecialEffect = fmSpecialEffectFlat
        header.BackColor = RGB(200, 200, 200)
        header.Height = 10

        ' align header to body (should be done last!)
        header.Width = body.Width
        header.Left = body.Left
        header.Top = body.Top - (header.Height - 1)
End Sub

Usage:

Private Sub UserForm_Activate()
    Call CreateListBoxHeader(Me.listBox_Body, Me.listBox_Header, Array("Header 1", "Header 2"))
End Sub
3
Jonas_Hess

J'aime utiliser l'approche suivante pour les en-têtes sur une zone de liste déroulante dans laquelle le CboBx n'est pas chargé à partir d'une feuille de calcul (données de SQL par exemple). La raison pour laquelle je ne spécifie pas dans une feuille de calcul est que je pense que le seul moyen de faire en sorte que RowSource fonctionne est si vous chargez à partir d'une feuille de calcul.

Cela fonctionne pour moi: 

  1. Créez votre ComboBox et créez un ListBox avec une disposition identique mais une seule ligne.
  2. Placez le ListBox directement sur le ComboBox.
  3. Dans votre VBA, chargez ListBox row1 avec les en-têtes souhaités.
  4. Dans votre VBA pour l'action yourListBoxName_Click, entrez le code suivant:

    yourComboBoxName.Activate`
    yourComboBoxName.DropDown`
    
  5. Lorsque vous cliquez sur la liste, la liste déroulante se déroulera et fonctionnera normalement tant que les en-têtes (dans la liste) resteront au-dessus de la liste.

2
RR_CodeSlinger

Je cherchais depuis longtemps une solution pour ajouter un en-tête sans utiliser de feuille distincte et tout copier dans le formulaire utilisateur. 

Ma solution consiste à utiliser la première ligne comme en-tête, à l'exécuter via une condition if et à ajouter des éléments supplémentaires en dessous. 

Comme ça: 

If lborowcount = 0 Then
 With lboorder
 .ColumnCount = 5
 .AddItem
 .Column(0, lborowcount) = "Item"
 .Column(1, lborowcount) = "Description"
 .Column(2, lborowcount) = "Ordered"
 .Column(3, lborowcount) = "Rate"
 .Column(4, lborowcount) = "Amount"
 End With
 lborowcount = lborowcount + 1
End If
        
        
With lboorder
 .ColumnCount = 5
 .AddItem
 .Column(0, lborowcount) = itemselected
 .Column(1, lborowcount) = descriptionselected
 .Column(2, lborowcount) = orderedselected
 .Column(3, lborowcount) = rateselected
 .Column(4, lborowcount) = amountselected
 
 
 End With

lborowcount = lborowcount + 1

dans cet exemple, lboorder est la liste, lborowcount compte à quelle ligne ajouter l'élément de liste suivant. C'est une liste déroulante de 5 colonnes. Pas idéal, mais cela fonctionne et lorsque vous devez faire défiler horizontalement, l'en-tête reste au-dessus de la ligne.

0
J. G.

Pourquoi ne pas simplement ajouter des étiquettes en haut de la liste et si des modifications sont nécessaires, la seule chose que vous devez modifier par programme sont les étiquettes.

0
Jim

Vous pouvez essayer ceci. Je suis assez nouveau sur le forum, mais je voulais offrir quelque chose qui a fonctionné pour moi car j'ai reçu autant d'aide de ce site par le passé. Il s’agit essentiellement d’une variante de ce qui précède, mais j’ai trouvé la solution plus simple.

Collez-le simplement dans la section Userform_Initialize de votre code userform. Notez que vous devez déjà avoir une zone de liste sur le formulaire utilisateur ou le créer dynamiquement au-dessus de ce code. Veuillez également noter que le tableau est une liste d’en-têtes (ci-dessous "Header1", "Header2", etc.). Remplacez-les par vos propres en-têtes. Ce code définira ensuite une barre d’en-tête en haut, en fonction de la largeur des colonnes de la liste Désolé, il ne défile pas - les étiquettes sont fixes. 

Plus de codeurs seniors - n'hésitez pas à commenter ou à améliorer cela.

    Dim Mywidths As String
    Dim Arrwidths, Arrheaders As Variant
    Dim ColCounter, Labelleft As Long
    Dim theLabel As Object                

    [Other code here that you would already have in the Userform_Initialize section]

    Set theLabel = Me.Controls.Add("Forms.Label.1", "Test" & ColCounter, True)
            With theLabel
                    .Left = ListBox1.Left
                    .Top = ListBox1.Top - 10
                    .Width = ListBox1.Width - 1
                    .Height = 10
                    .BackColor = RGB(200, 200, 200)
            End With
            Arrheaders = Array("Header1", "Header2", "Header3", "Header4")

            Mywidths = Me.ListBox1.ColumnWidths
            Mywidths = Replace(Mywidths, " pt", "")
            Arrwidths = Split(Mywidths, ";")
            Labelleft = ListBox1.Left + 18
            For ColCounter = LBound(Arrwidths) To UBound(Arrwidths)
                        If Arrwidths(ColCounter) > 0 Then
                                Header = Header + 1
                                Set theLabel = Me.Controls.Add("Forms.Label.1", "Test" & ColCounter, True)

                                With theLabel
                                    .Caption = Arrheaders(Header - 1)
                                    .Left = Labelleft
                                    .Width = Arrwidths(ColCounter)
                                    .Height = 10
                                    .Top = ListBox1.Top - 10
                                    .BackColor = RGB(200, 200, 200)
                                    .Font.Bold = True
                                End With
                                 Labelleft = Labelleft + Arrwidths(ColCounter)

                        End If
             Next
0
Peter Winn

Voici ma solution.

J'ai remarqué que lorsque je spécifie la source de la zone de liste dans la fenêtre de propriétés du VBE, les en-têtes ne s'affichent pas. Ce n'est que lorsque nous essayons de définir la source de lignes via du code VBA que les en-têtes sont perdus.

J'ai donc commencé par définir la source de lignes de la liste comme une plage nommée dans le VBE, via la fenêtre de propriétés, puis je peux réinitialiser la source de lignes dans le code VBA par la suite. Les en-têtes apparaissent toujours à chaque fois.

J'utilise ceci en combinaison avec une macro de filtrage avancée d'un listobject, qui crée ensuite un autre listobject (filtré) sur lequel la source de lignes est basée.

Cela a fonctionné pour moi

0
JamesSA

Voici une approche qui automatise la création d’étiquettes au-dessus de chaque colonne d’une zone de liste (sur une feuille de calcul).

Cela fonctionnera (mais pas super-joli!) Tant qu'il n'y a pas de barre de défilement horizontale sur votre liste.

Sub Tester()
Dim i As Long

With Me.lbTest
    .Clear
    .ColumnCount = 5
    'must do this next step!
    .ColumnWidths = "70;60;100;60;60"
    .ListStyle = fmListStylePlain
    Debug.Print .ColumnWidths
    For i = 0 To 10
        .AddItem
        .List(i, 0) = "blah" & i
        .List(i, 1) = "blah"
        .List(i, 2) = "blah"
        .List(i, 3) = "blah"
        .List(i, 4) = "blah"
    Next i

End With

LabelHeaders Me.lbTest, Array("Header1", "Header2", _
                     "Header3", "Header4", "Header5")

End Sub

Sub LabelHeaders(lb, arrHeaders)

    Const LBL_HT As Long = 15
    Dim T, L, shp As Shape, cw As String, arr
    Dim i As Long, w

    'delete any previous headers for this listbox
    For i = lb.Parent.Shapes.Count To 1 Step -1
        If lb.Parent.Shapes(i).Name Like lb.Name & "_*" Then
            lb.Parent.Shapes(i).Delete
        End If
    Next i

    'get an array of column widths
    cw = lb.ColumnWidths
    If Len(cw) = 0 Then Exit Sub
    cw = Replace(cw, " pt", "")
    arr = Split(cw, ";")

    'start points for labels
    T = lb.Top - LBL_HT
    L = lb.Left

    For i = LBound(arr) To UBound(arr)
        w = CLng(arr(i))
        If i = UBound(arr) And (L + w) < lb.Width Then w = lb.Width - L
        Set shp = ActiveSheet.Shapes.AddShape(msoShapeRectangle, _
                                         L, T, w, LBL_HT)
        With shp
            .Name = lb.Name & "_" & i
            'do some formatting
            .Line.ForeColor.RGB = vbBlack
            .Line.Weight = 1
            .Fill.ForeColor.RGB = RGB(220, 220, 220)
            .TextFrame2.TextRange.Characters.Text = arrHeaders(i)
            .TextFrame2.TextRange.Font.Size = 9
            .TextFrame2.TextRange.Font.Fill.ForeColor.RGB = vbBlack
        End With
        L = L + w
    Next i
End Sub
0
Tim Williams

Une autre variante de la réponse de Lunatik consiste à utiliser un booléen local et l'événement change afin que la ligne puisse être mise en surbrillance à l'initialisation, mais désélectionnée et bloquée après un changement de sélection effectué par l'utilisateur:

Private Sub lbx_Change()

    If Not bHighlight Then

        If Me.lbx.Selected(0) Then Me.lbx.Selected(0) = False

    End If

    bHighlight = False

End Sub

Lorsque la zone de liste est initialisée, vous définissez ensuite bHighlight et lbx.Selected (0) = True, ce qui permettra à la ligne d'en-tête d'initialiser la sélection; ensuite, la première modification sera désélectionnée et empêchera la sélection de la ligne à nouveau ...

0
ChE Junkie