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
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.
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é.
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 :)
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é.
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
Cette solution nécessite que vous ajoutiez un deuxième élément ListBox et le placiez au-dessus du premier.
Comme ça:
Ensuite, vous appelez la fonction CreateListBoxHeader pour corriger l'alignement et ajouter des éléments d'en-tête.
Résultat:
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
Private Sub UserForm_Activate()
Call CreateListBoxHeader(Me.listBox_Body, Me.listBox_Header, Array("Header 1", "Header 2"))
End Sub
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:
Dans votre VBA pour l'action yourListBoxName_Click, entrez le code suivant:
yourComboBoxName.Activate`
yourComboBoxName.DropDown`
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.
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.
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.
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
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
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
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 ...