运行时创建的VBA TextBox无法引用,如何保存其数据?
问题修复与解决方案
先解决最根本的错误:控件创建参数顺序搞反了
你写的Controls.Add语句参数顺序错了,VBA里Forms.Controls.Add的正确语法是:
Controls.Add(控件类型ProgID, 控件名称, 是否可见)
你当前的代码是DocumentsForm.Controls.Add("Forms.Textbox.1", True),把「是否可见」的True传到了「控件名称」的位置,导致控件的Name根本不是你后续赋值的DocName & contCount,而是被设成了字符串"True"——这就是为什么遍历控件时名称为空、直接引用Type1会报「Object Required」的核心原因。
第一步:修正控件创建代码
把创建控件的语句改成这样,直接在创建时指定正确名称:
Set txtbox = DocumentsForm.Controls.Add("Forms.Textbox.1", DocName & contCount, True)
后续的.Name赋值可以保留也可以删掉,作为双重保障。
第二步:正确引用动态控件
动态创建的控件是后期绑定的,VBA编译时不认识Type1这种直接写的名称,必须通过窗体的Controls集合按名称访问:
' 给Type1文本框赋值 Me.Controls("Type1").Value = "Test" ' 读取Status2的内容 Dim statusContent As String statusContent = Me.Controls("Status2").Value
第三步:批量保存用户输入
如果要把所有动态文本框的内容存起来(比如存到工作表),可以遍历控件集合,按命名规则筛选后写入:
Public Sub SaveAllInputs() Dim saveSheet As Worksheet Set saveSheet = ThisWorkbook.Sheets("数据存储") ' 替换为你的工作表名 Dim currentRow As Integer currentRow = 2 ' 假设表头在第一行,从第二行开始存 Dim ctrl As Control Dim lineNum As Integer Dim fieldName As String For Each ctrl In Me.Controls If TypeName(ctrl) = "TextBox" Then ' 从控件名拆分行号和字段名(比如Type1拆成字段Type、行号1) lineNum = Val(Right(ctrl.Name, Len(ctrl.Name) - InStrRev(ctrl.Name, "e"))) ' 根据你的命名规则调整提取逻辑 fieldName = Left(ctrl.Name, InStr(ctrl.Name, CStr(lineNum)) - 1) ' 找到表头对应的列 Dim targetCol As Integer targetCol = saveSheet.Rows(1).Find(fieldName, LookIn:=xlValues, LookAt:=xlWhole).Column ' 写入数据 saveSheet.Cells(currentRow + lineNum - 1, targetCol).Value = ctrl.Value End If Next ctrl End Sub
可选优化:用集合存动态控件(访问更高效)
如果要频繁操作这些动态控件,可以在创建时把它们放到一个集合里,后续直接从集合取,不用每次遍历控件:
' 在窗体模块顶部声明私有集合 Private dynamicTxtBoxes As Collection ' 修改AddLine_Click过程 Public Sub AddLine_Click() Static lineCount As Integer Dim txtbox As Object Dim docNames As Variant docNames = Array("Type", "Status", "Reference", "Units", "Quantity", "ValidityDate", "IssuingAuthority", "Reason") ' 第一次运行时初始化集合 If dynamicTxtBoxes Is Nothing Then Set dynamicTxtBoxes = New Collection End If lineCount = lineCount + 1 ' 用静态变量直接计数,比遍历控件高效 Dim prevWidth As Single prevWidth = 0 For Each docName In docNames Set txtbox = DocumentsForm.Controls.Add("Forms.Textbox.1", docName & lineCount, True) Dim templateWidth As Single templateWidth = DocumentsForm.Controls("Doc" & docName).Width With txtbox .Left = 10.5 + prevWidth .Height = 26.25 .Top = 47.25 + (26.25 * lineCount) .Width = templateWidth .SpecialEffect = fmSpecialEffectFlat .BorderStyle = fmBorderStyleSingle .BorderColor = &H80000000 End With ' 把控件加到集合,用名称当键 dynamicTxtBoxes.Add txtbox, docName & lineCount prevWidth = prevWidth + templateWidth Next End Sub ' 示例:从集合取控件操作 Public Sub UpdateType1() Dim typeBox As Object Set typeBox = dynamicTxtBoxes("Type1") typeBox.Value = "测试内容" ' 遍历集合打印所有内容 Dim item As Object For Each item In dynamicTxtBoxes Debug.Print item.Name & ": " & item.Value Next End Sub
内容的提问来源于stack exchange,提问作者Drawleeh
相关产品推荐
相关产品推荐

