[英]VBA Excel ListView Checkboxes do not show in Userform
I have a UserForm with a MultipageControl (name Controller_MultiPage).我有一个带有 MultipageControl(名称 Controller_MultiPage)的用户窗体。 At runtime my code adds pages to the Multipage and creates a newListView on each page.在运行时,我的代码将页面添加到 Multipage 并在每个页面上创建一个 newListView。 Every ListView has:每个 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
then I populate the newListView with data from an XML file:然后我用 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
but the checkboxes do not show.但复选框不显示。 I can see the space for them in front of the first column and by clicking that space the checkbox of that particular row then appears.我可以在第一列前面看到它们的空间,然后单击该空间,然后出现该特定行的复选框。 What I also noticed is that if I change the property我还注意到,如果我更改属性
listItem.Checked = True
the behavior described above does not change, and when I click the free space in front of the first column (checkboxes space) the chsckbox that then shows up is still unchecked.上述行为没有改变,当我单击第一列(复选框空间)前面的可用空间时,然后显示的 chsckbox 仍然未选中。 Any idea?任何想法?
The problem seems to be in the behavior of the MultiPage control.问题似乎出在 MultiPage 控件的行为中。 What I noticed was that if I forced the checkboxes' status (checked or unchecked) from the code, using the MultiPage_Change
event, then the checkboxes show up.我注意到的是,如果我使用MultiPage_Change
事件从代码中强制复选框的状态(选中或未选中),那么复选框就会出现。
So what I did was to create a class that holds the status of all checkboxes of all listviews on a single page, instantiate the Class for each ListView
and store everything into a Dictionary, using the newListView.Name
as Key所以我所做的是创建一个 class 保存单个页面上所有列表视图的所有复选框的状态,为每个ListView
实例化 Class 并将所有内容存储到字典中,使用newListView.Name
作为键
Then when the user changes page, the MultiPage_Change event that fires resets all the values of the checkboxes according to the Dictionary stored values.然后,当用户更改页面时,触发的 MultiPage_Change 事件会根据 Dictionary 存储的值重置复选框的所有值。
In the Listview_N_ItemChecked event some other code updates the status of the item stored in the Dictionary.在 Listview_N_ItemChecked 事件中,一些其他代码会更新存储在 Dictionary 中的项目的状态。 Kind of cumbersome but it works.有点麻烦,但它有效。
the class ( updated ): class(更新):
' 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
The Form relevant code.表单相关代码。 Module level:模块级别:
Dim myDict As New Dictionary ' the Dictionary
Dim ComponentsSignalsList As ComponentsSignalsRecord ' the Class
for each ListView created, may be one or more for every single MultiPage page:对于创建的每个 ListView,每个 MultiPage 页面可能是一个或多个:
Set ComponentsSignalsList = New ComponentsSignalsRecord
ComponentsSignalsList.setName = newListView.name
while populating the listview(s) in a loop for each single item added:在为添加的每个单个项目循环填充列表视图时:
ComponentsSignalsList.addSignal List_Item.Text, List_Item.Checked
end of each loop, add the Class instance to the Dictionary:在每个循环结束时,将 Class 实例添加到 Dictionary 中:
myDict.Add ComponentsSignalsList.getName, ComponentsSignalsList
Now when changing Page in the MultiPage widget:现在在 MultiPage 小部件中更改页面时:
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
when ticking/unticking a checkbox (note the the ItemChecked event handler is defined in another Class Public WithEvents
, where the handler calls this method passing both the ListView ID and the Item object):当勾选/取消勾选复选框时(注意 ItemChecked 事件处理程序是在另一个 Class 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
I just found the answer to the same problem that I also had and I feel so stupid.我刚刚找到了同样问题的答案,我觉得自己很愚蠢。 I had the first column of the Listview set to Width = 0... and thus the checkboxes would no longer show.我将 Listview 的第一列设置为 Width = 0...,因此复选框将不再显示。 I gave it a width and everithing is back to normal...我给了它一个宽度,一切都恢复了正常......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.