简体   繁体   English

如何使用 VBA 将标题添加到 Excel 用户表单中的多列列表框中

[英]How to add headers to a multicolumn listbox in an Excel userform using VBA

Is it possible to set up the headers in a multicolumn listbox without using a worksheet range as the source?是否可以在不使用工作表范围作为源的情况下在多列列表框中设置标题?

The following uses an array of variants which is assigned to the list property of the listbox, the headers appear blank.以下使用分配给列表框的列表属性的变量数组,标题显示为空白。

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

No. I create labels above the listbox to serve as headers.不,我在列表框上方创建标签作为标题。 You might think that it's a royal pain to change labels every time your lisbox changes.您可能认为每次 lisbox 更改时都更改标签是一件非常痛苦的事情。 You'd be right - it is a pain.你是对的 - 这是一种痛苦。 It's a pain to set up the first time, much less changes.第一次设置很痛苦,更不用说更改了。 But I haven't found a better way.但是我还没有找到更好的方法。

Here is my approach to solve the problem:这是我解决问题的方法:

This solution requires you to add a second ListBox element and place it above the first one.此解决方案要求您添加第二个 ListBox 元素并将其放在第一个元素之上。

Like this:像这样:

添加一个额外的列表框

Then you call the function CreateListBoxHeader to make the alignment correct and add header items.然后调用 function CreateListBoxHeader 以使 alignment 正确并添加 header 项。

Result:结果:

调用函数 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

I was looking at this problem just now and found this solution.我刚才在看这个问题并找到了这个解决方案。 If your RowSource points to a range of cells, the column headings in a multi-column listbox are taken from the cells immediately above the RowSource.如果您的RowSource指向一系列单元格,则多列列表框中的列标题取自 RowSource 正上方的单元格。

Using the example pictured here, inside the listbox, the words Symbol and Name appear as title headings.使用此处图示的示例,在列表框中,“符号”和“名称”这两个词显示为标题。 When I changed the word Name in cell AB1, then opened the form in the VBE again, the column headings changed.当我更改单元格 AB1 中的名称,然后再次在 VBE 中打开表单时,列标题发生了变化。

显示命名范围和范围之外的列标题的屏幕截图。

The example came from a workbook in VBA For Modelers by S. Christian Albright, and I was trying to figure out how he got the column headings in his listbox:)该示例来自 S. Christian Albright 的 VBA For Modelers 中的工作簿,我试图弄清楚他是如何在列表框中获得列标题的:)

Simple answer: no.简单的回答:没有。

What I've done in the past is load the headings into row 0 then set the ListIndex to 0 when displaying the form.我过去所做的是将标题加载到第 0 行,然后在显示表单时将 ListIndex 设置为 0。 This then highlights the "headings" in blue, giving the appearance of a header.然后以蓝色突出显示“标题”,呈现 header 的外观。 The form action buttons are ignored if the ListIndex remains at zero, so these values can never be selected.如果 ListIndex 保持为零,则忽略表单操作按钮,因此永远无法选择这些值。

Of course, as soon as another list item is selected, the heading loses focus, but by this time their job is done.当然,一旦选择了另一个列表项,标题就会失去焦点,但此时他们的工作已经完成。

Doing things this way also allows you to have headings that scroll horizontally, which is difficult/impossible to do with separate labels that float above the listbox.以这种方式做事还允许您拥有水平滚动的标题,这对于浮动在列表框上方的单独标签来说是困难/不可能的。 The flipside is that the headings do not remain visible if the listbox needs to scroll vertically.另一方面,如果列表框需要垂直滚动,标题不会保持可见。

Basically, it's a compromise that works in the situations I've been in.基本上,这是一种妥协,适用于我所经历的情况。

There is very easy solution to show headers at the top of multi columns list box.有一个非常简单的解决方案可以在多列列表框的顶部显示标题。 Just change the property value to "true" for "columnheads" which is false by default.只需将“columnheads”的属性值更改为“true”,默认情况下为 false。

After that Just mention the data range in property "rowsource" excluding header from the data range and header should be at first top row of data range then it will pick the header automatically and you header will be freezed. After that Just mention the data range in property "rowsource" excluding header from the data range and header should be at first top row of data range then it will pick the header automatically and you header will be freezed.

if suppose you have data in range "A1:H100" and header at "A1:H1" which is the first row then your data range should be "A2:H100" which needs to mention in property "rowsource" and "columnheads" perperty value should be true如果假设您在第一行的“A1:H1”处有“A1:H100”和 header 范围内的数据,那么您的数据范围应该是“A2:H100”,这需要在属性“rowsource”和“columnheads”属性中提及值应该为真

Regards, Asif Hameed问候, 阿西夫·哈米德

Just use two Listboxes, one for header and other for data只需使用两个列表框,一个用于 header,另一个用于数据

  1. for headers - set RowSource property to top row eg Incidents:Q4:S4对于标题 - 将 RowSource 属性设置为顶行,例如 Incidents:Q4:S4

  2. for data - set Row Source Property to Incidents:Q5:S10对于数据 - 将行源属性设置为 Incidents:Q5:S10

SpecialEffects to "3-frmSpecialEffectsEtched" “3-frmSpecialEffectsEtched”的特殊效果在此处输入图像描述

I like to use the following approach for headers on a ComboBox where the CboBx is not loaded from a worksheet (data from sql for example).我喜欢对 ComboBox 上的标头使用以下方法,其中未从工作表加载 CboBx(例如来自 sql 的数据)。 The reason I specify not from a worksheet is that I think the only way to get RowSource to work is if you load from a worksheet.我不从工作表中指定的原因是,我认为让 RowSource 工作的唯一方法是从工作表加载。

This works for me:这对我有用:

  1. Create your ComboBox and create a ListBox with an identical layout but just one row.创建您的 ComboBox 并创建一个具有相同布局但只有一行的 ListBox。
  2. Place the ListBox directly on top of the ComboBox.将 ListBox 直接放在 ComboBox 的顶部。
  3. In your VBA, load ListBox row1 with the desired headers.在您的 VBA 中,使用所需的标题加载 ListBox row1。
  4. In your VBA for the action yourListBoxName_Click, enter the following code:在您的 VBA 中为 yourListBoxName_Click 操作输入以下代码:

     yourComboBoxName.Activate` yourComboBoxName.DropDown`
  5. When you click on the listbox, the combobox will drop down and function normally while the headings (in the listbox) remain above the list.当您单击列表框时,combobox 将下拉,function 通常会下降,而标题(在列表框中)仍位于列表上方。

I was searching for quite a while for a solution to add a header without using a separate sheet and copy everything into the userform.我一直在寻找一种解决方案来添加 header 而不使用单独的工作表并将所有内容复制到用户表单中。

My solution is to use the first row as header and run it through an if condition and add additional items underneath.我的解决方案是将第一行用作 header 并通过 if 条件运行它并在下面添加其他项目。

Like that:像那样:

 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

in that example lboorder is the listbox, lborowcount counts at which row to add the next listbox item.在该示例中, lboorder 是列表框, lborowcount 计数添加下一个列表框项目的行。 It's a 5 column listbox.这是一个 5 列的列表框。 Not ideal but it works and when you have to scroll horizontally the "header" stays above the row.不理想,但它有效,当您必须水平滚动时,“标题”保持在行上方。

Here's my solution.这是我的解决方案。

I noticed that when I specify the listbox's rowsource via the properties window in the VBE, the headers pop up no problem.我注意到,当我通过 VBE 中的属性 window 指定列表框的行源时,标题弹出没有问题。 Its only when we try define the rowsource through VBA code that the headers get lost.只有当我们尝试通过 VBA 代码定义行源时,标头才会丢失。

So I first went a defined the listboxes rowsource as a named range in the VBE for via the properties window, then I can reset the rowsource in VBA code after that.所以我首先通过属性 window 将列表框行源定义为 VBE 中的命名范围,然后我可以在 VBA 代码中重置行源。 The headers still show up every time.标题仍然每次都出现。

I am using this in combination with an advanced filter macro from a listobject, which then creates another (filtered) listobject on which the rowsource is based.我将它与来自列表对象的高级过滤器宏结合使用,然后创建另一个(过滤的)列表对象,行源所基于的列表对象。

This worked for me这对我有用

Another variant on Lunatik's response is to use a local boolean and the change event so that the row can be highlighted upon initializing, but deselected and blocked after a selection change is made by the user: Lunatik 响应的另一个变体是使用本地 boolean 和更改事件,以便在初始化时可以突出显示该行,但在用户进行选择更改后取消选择并阻止:

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

When the listbox is initialized you then set bHighlight and lbx.Selected(0) = True, which will allow the header-row to initialize selected;初始化列表框后,您设置 bHighlight 和 lbx.Selected(0) = True,这将允许标题行初始化选中; afterwards, the first change will deselect and prevent the row from being selected again...之后,第一次更改将取消选择并防止再次选择该行...

Here's one approach which automates creating labels above each column of a listbox (on a worksheet).这是一种自动在列表框的每一列上方(在工作表上)创建标签的方法。

It will work (though not super-pretty.) as long as there's no horizontal scrollbar on your listbox.只要列表框上没有水平滚动条,它就可以工作(虽然不是非常漂亮。)。

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

You can give this a try.你可以试试这个。 I am quite new to the forum but wanted to offer something that worked for me since I've gotten so much help from this site in the past.我对论坛很陌生,但我想提供一些对我有用的东西,因为我过去从这个网站得到了很多帮助。 This is essentially a variation of the above, but I found it simpler.这本质上是上述的变体,但我发现它更简单。

Just paste this into the Userform_Initialize section of your userform code.只需将其粘贴到用户表单代码的 Userform_Initialize 部分。 Note you must already have a listbox on the userform or have it created dynamically above this code.请注意,您必须在用户窗体上已经有一个列表框,或者在此代码上方动态创建它。 Also please note the Array is a list of headings (below as "Header1", "Header2" etc. Replace these with your own headings. This code will then set up a heading bar at the top based on the column widths of the list box. Sorry it doesn't scroll - it's fixed labels.另请注意,数组是标题列表(以下为“Header1”、“Header2”等。将这些替换为您自己的标题。然后,此代码将根据列表框的列宽在顶部设置一个标题栏. 抱歉,它不滚动 - 它是固定标签。

More senior coders - please feel free to comment or improve this.更高级的编码人员 - 请随时发表评论或改进。

    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

This is a bummer.这是一个无赖。 Have to use an intermediate sheet to put the data in so Excel knows to grab the headers.必须使用中间表将数据放入,以便 Excel 知道获取标题。 But I wanted that workbook to be hidden so here's how I had to do the rowsource.但是我希望隐藏该工作簿,所以这就是我必须执行行源的方法。 Most of this code is just setting things up...大部分代码只是设置...

Sub listHeaderTest()
Dim ws As Worksheet
Dim testarr() As String
Dim numberOfRows As Long
Dim x As Long, n As Long

'example sheet
Set ws = ThisWorkbook.Sheets(1)
'example headers
For x = 1 To UserForm1.ListBox1.ColumnCount
    ws.Cells(1, x) = "header" & x
Next x
'example array dimensions
numberOfRows = 15
ReDim testarr(numberOfRows, UserForm1.ListBox1.ColumnCount - 1)
'example values for the array/listbox
For n = 0 To UBound(testarr)
    For x = 0 To UBound(testarr, 2)
        testarr(n, x) = "test" & n & x
    Next x
Next n

'put array data into the worksheet
ws.Range("A2").Resize(UBound(testarr), UBound(testarr, 2) + 1) = testarr

'provide rowsource
UserForm1.ListBox1.RowSource = "'[" & ws.Parent.Name & "]" & ws.Name & "'!" _
& ws.Range("A2").Resize(ws.UsedRange.Rows.Count - 1, ws.UsedRange.Columns.Count).Address

UserForm1.Show

End Sub

Why not just add Labels to the top of the Listbox and if changes are needed, the only thing you need to programmatically change are the labels.为什么不直接将标签添加到列表框的顶部,如果需要更改,您唯一需要以编程方式更改的就是标签。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM