循环选择用户窗体控件以在工作表中打印值

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.NamectlMap(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 附加控件内容以在用户窗体中重用它

使用 VBA 循环遍历工作表中的每个打印页面

vba日历控件只能在一个单元格用怎么办

如何“捕捉”到RefEdit选择的顶部

VBA:如果工作簿中的工作表名称等于从用户窗体中选择的组合框值,则复制该工作表并将其粘贴到另一个工作簿中

允许用户打印受保护的工作表