循环选择用户窗体控件以在工作表中打印值
Posted
技术标签:
【中文标题】循环选择用户窗体控件以在工作表中打印值【英文标题】:Looping through select userform controls to print values in sheet 【发布时间】:2020-01-07 18:03:53 【问题描述】:新更新 - 使用集合映射用户窗体控件的目标
使用集合来引用用户窗体控件和输入位置
我创建了一个集合,如下所示,以将打印用户窗体控件的位置引用到工作表。我在工作表“Sheet1”上对此进行了测试,控件为“subTaskID”和“TextBoxsubtask”。
我得到一个
"ByRef 参数类型不匹配"
在 ctlMap 变量上。我试图按照下面 PeterT 的解释进行操作。
'' COLLECTION START DELETE FROM HERE IF WRONG
Sub Userform_Intialize()
Set ctlMap = New Collection
With ctlMap
.Add item = "Sheet1!B1", key:="SubTaskID"
.Add item = "Sheet!B2", key:="TextBoxsubtask"
End With
End Sub
'Macro to populate sheet
Private Sub CommandButton1_Click()
Dim ctl As Variant
For Each ctl In Me.Controls
If ControlExists(ctlMap, ctl.Name) Then
Range(ctlMap(ctl.Name)) = ctl.Value
End If
Next ctl
End Sub
原始问题
我有一个用户表单来填充有关项目任务的 Excel 文件。
我创建了将每个控件链接到正确列和单元格编号的代码。我想提高我的代码效率以加快速度。我不希望输入标签和某些文本框值,并且如果控件为空白则跳到下一个。
我无法通过控件正确排序循环,因为它没有打印在所需的单元格中。
如何重新排列此订单?还有如何防止它为空值打印 false?
我创建了一个包含所需控件名称的数组,但出现错误
运行时需要相同的对象。
lastrowST = subtaskws.range("A" & Rows.Count).End(xlUp).row
Dim Ctrl As Control, col As Long, range As range, userformorder As Variant
With AddTask
userformorder = Array(.SubTaskID, .TextBoxsubtask, .ComboBoxDeliverableFormat, .TextBoxcheckedcomplete, .TextBoxformat, .TextBoxacceptancecriteria, .BudgetWorkloadTextBox, .AWLTextBox, .ComboBoxOwner, .TextBoxTDSNumber, .TextBoxMilestone, .TextBoxTargetDeliveryDate, .ComboBoxW, .ComboBoxI, .ComboBoxe, .TextBoxP, .TextBoxLevel, .TextBoxInputQuality, .TextBoxNewInput, .TextBoxDelay, .TextBoxInternalVV, .TextBoxReviewer, .TextBoxDelivered, .ComboBoxNumIterations, .ComboBoxAcceptance, .ComboBoxProgress, .ComboBoxStatus, .ComboBoxFlowChart, .TextBoxActivitySheet, .TextBoxEvidenceofDelivery, .TextBoxComments)
For Each range In subtaskws.range("A" & lastrowST + 1 & ":AE" & lastrowST + 1 & "")
For Each Ctrl.Value In userformorder
If Ctrl.Value <> "" Then
range.Value = Ctrl.Value
Else
End If
Next Ctrl
Next range
End With
【问题讨论】:
看起来你的循环是错误的。col
应该做什么?
col 只是列计数器,所以移动列。现在是多余的
我已经更新了 wuestion
您的数组是否包含控件名称?
对,就是数组中的名字
【参考方案1】:
我经常使用的一种方法是在控件和单元格之间创建映射。我为此使用Collection
并在Initialize
事件中设置地图:
Private Sub UserForm_Initialize()
Set ctlMap = New Collection
With ctlMap
.Add Item:="Sheet1!B1", key:="TextBox1"
.Add Item:="Sheet1!B2", key:="TextBox2"
.Add Item:="Sheet1!B3", key:="CheckBox1"
End With
End Sub
我的用户表单看起来像这样
工作表上的数据如下所示
所以现在要填充控件,它只是一个循环遍历用户窗体上的所有控件。因为我们使用的是控件映射,所以只填充了映射的控件。 Select
语句是必要的,因为并非所有控件都具有相同的属性来访问数据。例如,TextBox 使用 ctl.Text
,而 CheckBox 使用 ctl.Value
。
编辑:我的记忆有问题,我提到了我的一些旧代码 例如。 This answer 很好地解释了你为什么 想要使用
.Value
来访问控件的值。这表示 您可能可以删除Select
语句并替换所有 用一个简单的ctl.Value = Range(ctlMap(ctl.Name))
来照顾 所有作业。以防万一开发人员想要执行 具体逻辑基于控件,我留下Select
语句 完好无损。
Private Sub PopulateControls()
Dim ctl As Variant
For Each ctl In Me.Controls
If ControlExists(ctlMap, ctl.Name) Then
Select Case TypeName(ctl)
Case "TextBox"
ctl.Text = Range(ctlMap(ctl.Name))
Case "CheckBox"
ctl.Value = Range(ctlMap(ctl.Name))
End Select
End If
Next ctl
End Sub
或可选
Private Sub PopulateControls() Dim ctl As Variant For Each ctl In Me.Controls If ControlExists(ctlMap, ctl.Name) Then ctl.Value = Range(ctlMap(ctl.Name)) End If Next ctl End Sub Private Sub ControlsToWorksheet() Dim ctl As Variant For Each ctl In Me.Controls If ControlExists(ctlMap, ctl.Name) Then Range(ctlMap(ctl.Name)) = ctl.Value End If Next ctl End Sub
用户表单的完整代码如下
Option Explicit
Private ctlMap As Collection
Private Sub PopulateControls()
Dim ctl As Variant
For Each ctl In Me.Controls
If ControlExists(ctlMap, ctl.Name) Then
Select Case TypeName(ctl)
Case "TextBox"
ctl.Text = Range(ctlMap(ctl.Name))
Case "CheckBox"
ctl.Value = Range(ctlMap(ctl.Name))
End Select
End If
Next ctl
End Sub
Private Sub ControlsToWorksheet()
Dim ctl As Variant
For Each ctl In Me.Controls
If ControlExists(ctlMap, ctl.Name) Then
Select Case TypeName(ctl)
Case "TextBox"
Range(ctlMap(ctl.Name)) = ctl.Text
Case "CheckBox"
Range(ctlMap(ctl.Name)) = ctl.Value
End Select
End If
Next ctl
End Sub
Private Function ControlExists(ByRef thisCollection As Collection, _
ByVal key As String) As Boolean
On Error GoTo SKIP
ctlMap.Item key
ControlExists = True
SKIP:
End Function
Private Sub CloseButton_Click()
Me.Hide
End Sub
Private Sub PopulateButton_Click()
PopulateControls
End Sub
Private Sub WriteButton_Click()
ControlsToWorksheet
End Sub
Private Sub UserForm_Initialize()
Set ctlMap = New Collection
With ctlMap
.Add Item:="Sheet1!B1", key:="TextBox1"
.Add Item:="Sheet1!B2", key:="TextBox2"
.Add Item:="Sheet1!B3", key:="CheckBox1"
End With
End Sub
【讨论】:
@SJR 感谢您的提醒。我已经用解释更新了答案中的代码。 感谢 PeterT,我希望使用您的一些建议来看看它是否加快了进程!您可以循环将用户表单名称放入数组中,然后遍历数组以节省指定每种情况的时间吗? 使用PopulateControls
的可选版本,您无需指定每种情况。只需在创建表单时初始化地图集合,如示例所示。
对不起,我很困惑,我正在关注工作表功能的控件,因为我希望将用户窗体中的值放入工作表中
我更新了上面的代码以显示可选的ControlsToWorksheet
sub 而没有Case
语句。不过,我不太确定您的最后评论。插入一些Debug.Print
语句以找出ctl.Name
和ctlMap(ctl.Name)
的值是什么。如果它们不是您所期望的,您应该重新检查您是如何初始化地图的。【参考方案2】:
我认为这会满足您的需求。彼得的回答中有一些值得细读的好东西。
lastrowST = subtaskws.range("A" & Rows.Count).End(xlUp).Row
Dim Ctrl As Variant, range1 As range, userformorder As Variant, col As Long
userformorder = Array("SubTaskID", "TextBoxsubtask", "ComboBoxDeliverableFormat", "TextBoxcheckedcomplete") 'etc
For Each Ctrl In userformorder
If AddTask.Controls(Ctrl).Value <> "" Then
subtaskws.range("A" & lastrowST + 1).Offset(, col).Value = AddTask.Controls(Ctrl).Value
End If
col = col + 1
Next Ctrl
End With
【讨论】:
感谢工作太棒了!彼得的东西看起来不错,但我不太明白! 问题,如何让代码跳过某些列,例如第 7 列 我可以将范围更改为上升到第 7 列,然后从第 9 列重新开始 您可以检查col
的值,如果是 7,则添加 2 而不是 1。
如果有帮助,您可能希望开始养成接受 SO 答案的习惯。以上是关于循环选择用户窗体控件以在工作表中打印值的主要内容,如果未能解决你的问题,请参考以下文章
清除 WebBrowser 附加控件内容以在用户窗体中重用它