vba 循环遍历形状列表框(更改类型)

vba Looping through Shape Listbox (change type)

所以我有这个带有几个列表框的电子表格。在这些列表框中,我有一些 values/items 实际上是过滤器。我想让每个列表框的每个 item/filter 修改我代码中的 SQL 查询。 所以我被要求遍历列表框,我设法通过循环电子表格的形状来做到这一点,但最终......这些列表框现在被视为 VBA 中的形状,而不是列表框了。我正在寻找一种方法来在列表框中转动我的形状,或者从 Shapes 类型中找到一种方法来循环每个列表框的项目。这是我的代码的一部分,到目前为止,我遍历每个 shapes/listboxes,如果在我的形状名称中有单词“CFRA”,那么我想在我的列表框中选择的每个项目中循环,以便我的函数 return他们。

Private Function getListFilters() As String

    My_Sheet.Activate
    Dim Shp
    For Each Shp In My_Sheet.Shapes
        pos = InStrRev(Shp.Name, "CFRA", , vbTextCompare)
        MsgBox (pos)
        If pos <> 0 Then
        MsgBox (TypeName(Shp))

        End If
    Next
End Function

提前感谢那些愿意帮助我的人,祝你今天愉快:)

由于您没有说明要从列表框中提取什么,请尝试下一个Function。它将提供名称中包含“CFRA”字符串的列表框对象。当然可以使用任何字符串:

Private Function getListObjX(strPartName As String, sh As Worksheet) As MSForms.ListBox
    Dim oObj As OLEObject
    For Each oObj In sh.OLEObjects
      If oObj.Name Like "*" & strPartName & "*" Then
          'Debug.Print oObj.Name, TypeName(oObj.Object): Stop
          If TypeName(oObj.Object) = "ListBox" Then
             Set getListObjX = oObj.Object: Exit Function
          End If
      End If
   Next
End Function

可以通过以下方式调用:

Sub testGetListObj()
  Dim sh As Worksheet, lstB As MSForms.ListBox, lstBF As ListBox
  Dim i As Long, arrSel As Variant, k As Long
   Set sh = ActiveSheet
   
   Set lstB = getListObjX("CFRA", sh)
   If lstB Is Nothing Then MsgBox "No such an ActiveX list box...": Exit Sub
   ReDim arrSel(lstB.ListCount - 1)
   
   For i = 0 To lstB.ListCount - 1
        If lstB.Selected(i) Then
            'Debug.Print lstB.List(i)
            arrSel(k) = lstB.List(i): k = k + 1
        End If
   Next i
   ReDim Preserve arrSel(k - 1)
   MsgBox Join(arrSel, "|")
End Sub

但是,作为 ActiveX 列表框类型,您可以简单地使用其事件之一。当然,如果您不需要从多个列表框中取项...

我还为 return 表单列表框的对象准备了一个函数(在您澄清问题之前)。也许,别人会用它...

    Dim oObj As ListBox
    For Each oObj In sh.ListBoxes 'even not being shown by intellisense, this collection exists...
      If oObj.Name Like "*" & strPartName & "*" Then
          'Debug.Print oObj.Name
          Set getListObjF = oObj: Exit Function
      End If
   Next
End Function

同样可以调用,但是lstB应该声明为As ListBox

已编辑,使功能一步到位

Private Function getListFilters(strPartName) As String
   Dim sh As Worksheet, lstB As MSForms.ListBox
   Dim oObj As OLEObject, i As Long, arrSel As Variant, k As Long
   
   Set sh = ActiveSheet ' use here your sheet
   
    For Each oObj In sh.OLEObjects
      If oObj.Name Like "*" & strPartName & "*" Then
          If TypeName(oObj.Object) = "ListBox" Then
             Set lstB = oObj.Object: Exit For
          End If
      End If
   Next
   If lstB Is Nothing Then MsgBox "No such an ActiveX list box...": Exit Function
   ReDim arrSel(lstB.ListCount - 1)
   
   For i = 0 To lstB.ListCount - 1
        If lstB.Selected(i) Then
            arrSel(k) = lstB.List(i): k = k + 1
        End If
   Next i
   ReDim Preserve arrSel(k - 1)
   getListFilters = Join(arrSel, "|")
End Function

并且该函数将被简单地称为:

Debug.Print getListFilters("CFRA")

您通过OLEObjects-作品集sheet访问ActiveX-对象。有趣的控制信息在这样一个对象的属性Object中:

  • 使用 VBA 函数 TypeName 确定您拥有哪种 OLE 对象

  • 可以使用 Object.ListCount 属性.

    获取的项目数
  • 要访问列表框的项目,循环遍历 Object.list 属性(索引从 0 开始,因此循环必须 运行 从 0 到 ListCount- 1)

  • 要检查一个项目是否被选中,使用匹配.Object.Selected 属性.

以下代码将循环打印作品所有列表框中的所有选定项目sheet:

Sub listBoxes()
    Dim objx As OLEObject
    For Each objx In ActiveSheet.OLEObjects
        Debug.Print "Name = " & objx.Name & "  Typ = " & TypeName(objx.Object)
        If TypeName(objx.Object) = "ListBox" Then
            Dim i As Long
            For i = 0 To objx.Object.ListCount - 1
                If objx.Object.Selected(i) Then
                    Debug.Print objx.Name, objx.Object.list(i)
                End If
            Next i
        End If
    Next
End Sub

Update:显示 ShapesOleObjects 和 [=61= 之间的连贯性]ActicX 控制 sheet:

A Shape 是所有不属于 cell/range 的容器。可以是任何一种绘制的形状形式(矩形、箭头、星星...),可以是图像、图表、OLEObject、表单控件等。

OLEObject 不是来自 Excel 而是使用称为 OLE 的技术放入 Excel sheet 中的东西, Object Linking and Embedding.

ActiveX 是一个控件(编辑框、列表框...)。这些控件由 Microsoft 开发,旨在 运行 在不同的环境(例如浏览器)中使用。它们可以通过 dll 访问,并且此 dll 已添加到 Excel 和其他办公程序中。

每个 ActiveX 控件都作为 OLEObject 添加到 sheet 中,但您也可以拥有不是 ActiceX 对象的不同 OLEObject(例如嵌入的 Word 文档)。

当你想通过 VBA 访问这些东西时,你可以使用 Shapes 集合,它列出了 sheet 的所有形状(包括所有 OLEObjects),或者你可以使用列出所有 OLEObject(包括所有 ActiveX 控件)的 OLEObjects 集合。但是,没有ActiveX集合,所以如果你想获取所有的ActiceX-Controls,你必须循环上面提到的两个集合。

如果要从形状集合中访问 OLEObject,首先需要检查形状的类型,它的类型必须是 msoOLEControlObject (12) 或 msoEmbeddedOLEObject (7) .可以找到所有形状类型的列表 here

如果形状是 7 或 12,您可以使用 Shape.OLEFormat.Object 访问 OLEObject。以下循环结果完全相同(ws 只是一个 worksheet 变量)

Dim sh As Shape, oleObj As OLEObject
For Each sh In ws.Shapes
    If sh.Type = msoOLEControlObject Or sh.Type = msoEmbeddedOLEObject Then
        Set oleObj = sh.OLEFormat.Object
        Debug.Print oleObj.Name, oleObj.OLEType
    End If
Next

For Each oleObj In ws.OLEObjects
    Debug.Print oleObj.Name, oleObj.OLEType
Next

注意 sh.Name 和 sh.OLEFormat.Object.Name 不一定相同。

现在最后一步是找到特定类型的 ActiveX 控件,这已经在上面的原始答案的代码中显示 - 可以通过 oleObj.object 访问 ActiveX 控件。如果 VBA 函数 TypeName 过滤掉例如您的列表框,请检查对象类型。