【问题标题】:VBA Excel ListView Checkboxes do not show in UserformVBA Excel ListView 复选框不显示在用户窗体中
【发布时间】:2020-10-09 13:07:32
【问题描述】:

我有一个带有 MultipageControl(名称 Controller_MultiPage)的用户窗体。 在运行时,我的代码将页面添加到 Multipage 并在每个页面上创建一个 newListView。 每个 ListView 都有:

      With newListView
        .MultiSelect = False
        .Width = Controller_MultiPage.Width - 10
        .Height = Controller_MultiPage.Height - 20
        .View = lvwReport
        .HideColumnHeaders = False
        .ColumnHeaders.Add Text:="Signal Name", Width:=.Width / 10 * 4
        .ColumnHeaders.Add Text:="Type", Width:=.Width / 10
        .ColumnHeaders.Add Text:="I/O", Width:=.Width / 10
        .ColumnHeaders.Add Text:="Description", Width:=.Width / 10 * 4
        .CheckBoxes = True
        .FullRowSelect = True
    End With

然后我用 XML 文件中的数据填充 newListView:

         For Each node In list
            With node.Attributes
                Set listItem = newListView.ListItems.Add(Text:=.getNamedItem("Name").Text)
                listItem.ListSubItems.Add = .getNamedItem("Type").Text
                listItem.ListSubItems.Add = IIf(.getNamedItem("Input").Text = "1", "IN", "OUT")
                listItem.ListSubItems.Add = .getNamedItem("Description").Text
                listItem.Checked = False
            End With
        Next

但复选框不显示。我可以在第一列前面看到它们的空间,然后单击该空间,然后出现该特定行的复选框。我还注意到,如果我更改属性

listItem.Checked = True

上述行为没有改变,当我单击第一列(复选框空间)前面的可用空间时,随后显示的 chsckbox 仍未选中。 有什么想法吗?

【问题讨论】:

  • 到目前为止没有人回答,但我正在找到一种解决方法,有点麻烦,但这是我能想到的最好的方法。测试后我会发布描述代码

标签: excel vba listview checkbox


【解决方案1】:

问题似乎出在 MultiPage 控件的行为上。 我注意到的是,如果我使用 MultiPage_Change 事件从代码中强制复选框的状态(选中或未选中),则会显示复选框。

所以我所做的是创建一个类,该类保存单个页面上所有列表视图的所有复选框的状态,为每个 ListView 实例化该类并将所有内容存储到字典中,使用 newListView.Name 作为键

然后当用户更改页面时,触发的 MultiPage_Change 事件会根据 Dictionary 存储的值重置所有复选框的值。

在 Listview_N_ItemChecked 事件中,一些其他代码会更新存储在 Dictionary 中的项目的状态。 有点麻烦,但很管用。

类(更新):

' Class Name = ComponentsSignalsRecord
Option Explicit

Dim Name As String
' NOTE: Signals(0) will always be empty and status(0) will always be False
Dim Signals() As String
Dim Status() As Boolean
Dim Component As String

Property Let SetComponentName(argName As String)
    Component = argName
End Property

Property Get GetComponentName() As String
    GetComponentName = Component
End Property

Property Get getSignalName(argIndex) As String
    If argIndex >= LBound(Signals) And argIndex <= UBound(Signals) Then
        getSignalName = Signals(argIndex)
    Else
        getSignalName = vbNullString
    End If
End Property

Property Get dumpAll() As String()
    dumpAll = Signals
End Property

Property Get Count() As Long
    Count = UBound(Signals)
End Property

Property Get getStatus(argName As String) As Integer
    ' returns: -1 = Not Found; 1 = True; 0 = False
    getStatus = -1
    Dim i As Integer
    For i = 0 To UBound(Signals)
        If argName = Signals(i) Then getStatus = IIf(Status(i) = True, 1, 0): Exit For
    Next
End Property

Property Let setName(argName As String)
    Name = argName
End Property

Property Get getName() As String
    getName = Name
End Property

Public Sub UncheckAll()
    Dim i As Integer
    For i = 0 To UBound(Status)
        Status(i) = False
    Next
End Sub

Public Sub CheckAll()
    Dim i As Integer
    For i = 0 To UBound(Status)
        Status(i) = True
    Next
End Sub

Public Sub deleteSignal(argName As String)
    Dim spoolSignals() As String
    Dim spoolStatus() As Boolean
    Dim i As Integer
    spoolSignals = Signals
    spoolStatus = Status
    ReDim Signals(0)
    ReDim Status(0)
    For i = 1 To UBound(spoolSignals)
        If argName <> spoolSignals(i) Then
            ReDim Preserve Signals(UBound(Signals) + 1):    Signals(UBound(Signals)) = spoolSignals(i)
            ReDim Preserve Status(UBound(Status) + 1):      Status(UBound(Status)) = spoolStatus(i)
        End If
    Next
End Sub

Public Sub addSignal(argName As String, argValue As Boolean)
    Dim i As Integer
    For i = 0 To UBound(Signals)
        If argName = Signals(i) Then GoTo bye
    Next
    ReDim Preserve Signals(UBound(Signals) + 1)
    ReDim Preserve Status(UBound(Status) + 1)
    Signals(UBound(Signals)) = argName
    Status(UBound(Status)) = argValue
bye:
End Sub

Public Sub setStatus(argName As String, argValue As Boolean)
    Dim i As Integer
    For i = 0 To UBound(Signals)
        If argName = Signals(i) Then Status(i) = argValue: Exit For
    Next
End Sub

Private Sub Class_Initialize()
    ReDim Signals(0)
    ReDim Status(0)
End Sub

表单相关代码。模块级别:

Dim myDict As New Dictionary                           ' the Dictionary
Dim ComponentsSignalsList As ComponentsSignalsRecord   ' the Class

对于创建的每个 ListView,每个 MultiPage 页面可能是一个或多个:

Set ComponentsSignalsList = New ComponentsSignalsRecord
ComponentsSignalsList.setName = newListView.name

在循环中为添加的每个单个项目填充列表视图时:

ComponentsSignalsList.addSignal List_Item.Text, List_Item.Checked

每个循环结束,将 Class 实例添加到 Dictionary:

myDict.Add ComponentsSignalsList.getName, ComponentsSignalsList

现在在多页小部件中更改页面时:

Private Sub Controller_MultiPage_Change()
    If isLoading Then Exit Sub   'avoid errors and undue behavior while initializing the MultiPage widget
    Dim locControl As Control
    Dim controlType As String: controlType = "ListView"
    With Controller_MultiPage
        For Each locControl In .Pages(.value).Controls
            If InStr(1, TypeName(locControl), controlType) > 0 Then
                Call Check_CheckBoxes(locControl)
            End If
        Next
    End With
End Sub

Private Sub Check_CheckBoxes(argListView As listView)
    If argListView.CheckBoxes = False Then Exit Sub   'some ListViews don't have checkboxes
    Dim myItem As ListItem
    For Each myItem In argListView.ListItems
        With myItem
            .Checked = myDict.Item(argListView.name).getStatus(.Text)
        End With
    Next
End Sub

勾选/取消勾选复选框时(注意 ItemChecked 事件处理程序是在另一个类 Public WithEvents 中定义的,其中处理程序调用此方法并传递 ListView ID 和 Item 对象):

Public Sub ListViewsEvents_ItemCheck(argListView As listView, argItem As MSComctlLib.ListItem)
    With argItem
        myDict.Item((argListView .name).setStatus argName:=.Text, argValue:=.Checked
    End With
End Sub

【讨论】:

  • 忘了说:两个ComponentsSignalsRecord 类数组没有使用Index = 0,即signals(0) 始终为空,status(0) 始终为False。此外,我实际上最终为该类添加了更多属性和方法,因为我发现 Dictionary of Classes 的这种结构甚至在我的应用程序的其他地方也很有用
  • 实际上Status(0) 会在调用 checkAll 方法时变为True,但又不应该使用数组的index(0)
【解决方案2】:

我刚刚找到了同样问题的答案,我觉得自己很愚蠢。我将 Listview 的第一列设置为 Width = 0...,因此复选框将不再显示。 我给了它一个宽度,一切都恢复了正常......

【讨论】:

    猜你喜欢
    • 2017-06-15
    • 2015-01-17
    • 2017-05-08
    • 2019-07-23
    • 1970-01-01
    • 2023-03-10
    • 2020-11-12
    • 1970-01-01
    • 2014-02-09
    相关资源
    最近更新 更多