尝试访问在 VBA 中运行时创建的控件的属性时出现运行时错误 (Visio)
Runtime error when trying to access properties of a control created at runtime in VBA (Visio)
在 Visio 中,我有一个用户窗体,其中填充了一些基于 Visio 符号的标签、按钮和文本框。标签、按钮和文本框是在 运行 时创建的。 Here is a snip of the form created
当您单击按钮时,目的是将标题从标签复制到文本框中。我已经创建了代码和按钮事件,因此我可以识别被单击的按钮,但是,当我尝试引用标签或文本框时,我得到“Run-time 错误‘-2147024809 (80070057)’:找不到指定的object.
这是我在 运行 时间创建控件的部分代码:
Set dynLabel = frameInputs.Controls.Add("Forms.Label.1", "dynLabel" & CStr(s.Index), True)
Set dynTextBox = frameInputs.Controls.Add("Forms.TextBox.1", "dynTextBox" & CStr(s.Index), True)
Set dynXferLabelButton = frameInputs.Controls.Add("Forms.CommandButton.1", "dynXferLabelButton" & CStr(s.Index), True)
我使用一些不同的 WITH 语句来设置每个控件的位置等。我还将 s.Index 值分配给 TAG 属性,这样我就可以确定稍后单击的是哪个按钮。
With dynLabel
.Top = ctrlTop
.Left = ctrlLeft
.Caption = ctrlText
.Tag = s.Index
End With
With dynTextBox
.Top = ctrlTop
.Left = ctrlLeft + 80
.Text = ctrlText
.Tag = s.Index
End With
With dynXferLabelButton
.Top = ctrlTop
.Left = ctrlLeft + 60
.Caption = ">>"
.Width = 20
.Height = 17
.FONTSIZE = 6
.Tag = s.Index
End With
我有一个名为“ButtonEvents”的 class 并使用以下代码在按钮上创建点击事件:
Dim cmdArray() As New ButtonEvents
...
...
ReDim Preserve cmdArray(i)
Set cmdArray(i).cmdEvents = dynXferLabelButton
作为一个简单的测试,这是我的按钮点击事件。当您单击按钮时,事件会触发,我可以从按钮中获取 TAG - 一切正常。使用 TAG,然后我可以确定 Label 控件的名称,我想我可以访问 Label 的属性:
Private Sub cmdEvents_Click()
MsgBox cmdEvents.Tag
MsgBox frmSetDevice.frameInputs.Controls.Item("dynLabel" & cmdEvents.Tag).Caption
Dim c As Control
For Each c In frmSetDevice.frameInputs.Controls
MsgBox c.Name
Next
End Sub
当我尝试使用 frmSetDevice.frameInputs.Controls.Item("dynLabel" & cmdEvents.Tag).Caption
或任何其他 属性 时出现运行时错误。如果我注释掉该行并让它 运行 通过循环向我展示控件,它找不到任何东西。
如果我在 UserForm_Activate 函数中的控件都创建完成后循环,它可以完美地找到所有控件。在我看来,一旦创建了控件并且 UserForm_Activate 事件结束,它们就无法再访问了。我想我需要做其他事情才能使它们易于访问吗?我做错了什么?
这是完整代码(删除了 un-related 函数和事件):
用户窗体 frmSetDevice
Dim cmdArray() As New ButtonEvents
Private Sub UserForm_Activate()
'MsgBox "Activate: " & DeviceCodeValue
Dim cIn, cOut As Integer
Dim ctrlLeft, ctrlTop As Integer
Dim ctrlText As String
If DeviceCodeValue <> 0 Then textCode.Text = DeviceCodeValue
If DeviceDescriptionValue <> 0 Then comboDevices = DeviceDescriptionValue
Set dataCollection = Nothing
FindShapeData ActivePage.Shapes(Me.DeviceObject), "Label"
Erase Labels
Labels = toArray(dataCollection)
Dim s As Visio.Shape
For i = 0 To UBound(Labels)
For Each s In ActivePage.Shapes(DeviceObjectName).Shapes
If s.Name = Labels(i) Then
'MsgBox GetShapeData(s, "Category")
Dim dynLabel As Control
Dim dynTextBox As Control
Dim dynXferLabelButton As Control
If InStr(Labels(i), "In") > 0 Then
cIn = cIn + 1
ctrlLeft = 20
ctrlTop = (20 * cIn)
Set dynLabel = frameInputs.Controls.Add("Forms.Label.1", "dynLabel" & CStr(s.Index), True)
Set dynTextBox = frameInputs.Controls.Add("Forms.TextBox.1", "dynTextBox" & CStr(s.Index), True)
Set dynXferLabelButton = frameInputs.Controls.Add("Forms.CommandButton.1", "dynXferLabelButton" & CStr(s.Index), True)
Else:
cOut = cOut + 1
ctrlLeft = 20
ctrlTop = (20 * cOut)
Set dynLabel = frameOutputs.Controls.Add("Forms.Label.1", "dynLabel" & CStr(s.Index), True)
Set dynTextBox = frameOutputs.Controls.Add("Forms.TextBox.1", "dynTextBox" & CStr(s.Index), True)
Set dynXferLabelButton = frameOutputs.Controls.Add("Forms.CommandButton.1", "dynXferLabelButton" & CStr(s.Index), True)
End If
ctrlText = s.Text
If LabelDataValue(s.Index) <> "" Then ctrlText = LabelDataValue(s.Index)
With dynLabel
.Top = ctrlTop
.Left = ctrlLeft
.Caption = ctrlText
.Tag = s.Index
End With
If GetShapeData(s, "Label") = 0 Then
ctrlText = s.Text
Else:
ctrlText = GetShapeData(s, "Label")
End If
With dynTextBox
.Top = ctrlTop
.Left = ctrlLeft + 80
.Text = ctrlText
.Tag = s.Index
End With
With dynXferLabelButton
.Top = ctrlTop
.Left = ctrlLeft + 60
.Caption = ">>"
.Width = 20
.Height = 17
.FONTSIZE = 6
.Tag = s.Index
End With
ReDim Preserve cmdArray(i)
Set cmdArray(i).cmdEvents = dynXferLabelButton
Exit For
End If
Next
Next i
Dim totalLines As Integer
If cIn >= cOut Then
totalLines = cIn
Else:
totalLines = cOut
End If
Me.Height = (25 * totalLines) + 150
frameInputs.Height = (25 * totalLines)
frameOutputs.Height = (25 * totalLines)
If Me.Height < 330 Then Me.Height = 330
cmdCancel.Top = Me.Height - 60
cmdSetDevice.Top = Me.Height - 60
Dim c As Control
For Each c In Me.frameInputs.Controls
MsgBox c.Name
Next
End Sub
Class 按钮事件
Public WithEvents cmdEvents As MSForms.CommandButton
Private Sub cmdEvents_Click()
MsgBox cmdEvents.Tag
MsgBox frmSetDevice.frameInputs.Controls.Item("dynLabel" & cmdEvents.Tag).Caption
Dim c As Control
For Each c In frmSetDevice.frameInputs.Controls
MsgBox c.Name
Next
End Sub
frmSetDevice
指的是“基本”用户窗体,而不是显示的实例。
如果您在 class 中为相应的 Label 和 Textbox 对象添加字段,那么您可以在 Click 事件中使用它们,而不必按名称查找它们
Public WithEvents cmdEvents As MSForms.CommandButton
Public lbl As MSForms.Label 'populate these when you populate cmdEvents
Public txt As MSForms.Textbox
Private Sub cmdEvents_Click()
MsgBox lbl.Caption 'etc etc
End Sub
我喜欢使用全局集合来保存这些类型的事件处理对象:您可以 .Add
而无需保持计数和调整数组大小。
在 Visio 中,我有一个用户窗体,其中填充了一些基于 Visio 符号的标签、按钮和文本框。标签、按钮和文本框是在 运行 时创建的。 Here is a snip of the form created
当您单击按钮时,目的是将标题从标签复制到文本框中。我已经创建了代码和按钮事件,因此我可以识别被单击的按钮,但是,当我尝试引用标签或文本框时,我得到“Run-time 错误‘-2147024809 (80070057)’:找不到指定的object.
这是我在 运行 时间创建控件的部分代码:
Set dynLabel = frameInputs.Controls.Add("Forms.Label.1", "dynLabel" & CStr(s.Index), True)
Set dynTextBox = frameInputs.Controls.Add("Forms.TextBox.1", "dynTextBox" & CStr(s.Index), True)
Set dynXferLabelButton = frameInputs.Controls.Add("Forms.CommandButton.1", "dynXferLabelButton" & CStr(s.Index), True)
我使用一些不同的 WITH 语句来设置每个控件的位置等。我还将 s.Index 值分配给 TAG 属性,这样我就可以确定稍后单击的是哪个按钮。
With dynLabel
.Top = ctrlTop
.Left = ctrlLeft
.Caption = ctrlText
.Tag = s.Index
End With
With dynTextBox
.Top = ctrlTop
.Left = ctrlLeft + 80
.Text = ctrlText
.Tag = s.Index
End With
With dynXferLabelButton
.Top = ctrlTop
.Left = ctrlLeft + 60
.Caption = ">>"
.Width = 20
.Height = 17
.FONTSIZE = 6
.Tag = s.Index
End With
我有一个名为“ButtonEvents”的 class 并使用以下代码在按钮上创建点击事件:
Dim cmdArray() As New ButtonEvents
...
...
ReDim Preserve cmdArray(i)
Set cmdArray(i).cmdEvents = dynXferLabelButton
作为一个简单的测试,这是我的按钮点击事件。当您单击按钮时,事件会触发,我可以从按钮中获取 TAG - 一切正常。使用 TAG,然后我可以确定 Label 控件的名称,我想我可以访问 Label 的属性:
Private Sub cmdEvents_Click()
MsgBox cmdEvents.Tag
MsgBox frmSetDevice.frameInputs.Controls.Item("dynLabel" & cmdEvents.Tag).Caption
Dim c As Control
For Each c In frmSetDevice.frameInputs.Controls
MsgBox c.Name
Next
End Sub
当我尝试使用 frmSetDevice.frameInputs.Controls.Item("dynLabel" & cmdEvents.Tag).Caption
或任何其他 属性 时出现运行时错误。如果我注释掉该行并让它 运行 通过循环向我展示控件,它找不到任何东西。
如果我在 UserForm_Activate 函数中的控件都创建完成后循环,它可以完美地找到所有控件。在我看来,一旦创建了控件并且 UserForm_Activate 事件结束,它们就无法再访问了。我想我需要做其他事情才能使它们易于访问吗?我做错了什么?
这是完整代码(删除了 un-related 函数和事件):
用户窗体 frmSetDevice
Dim cmdArray() As New ButtonEvents
Private Sub UserForm_Activate()
'MsgBox "Activate: " & DeviceCodeValue
Dim cIn, cOut As Integer
Dim ctrlLeft, ctrlTop As Integer
Dim ctrlText As String
If DeviceCodeValue <> 0 Then textCode.Text = DeviceCodeValue
If DeviceDescriptionValue <> 0 Then comboDevices = DeviceDescriptionValue
Set dataCollection = Nothing
FindShapeData ActivePage.Shapes(Me.DeviceObject), "Label"
Erase Labels
Labels = toArray(dataCollection)
Dim s As Visio.Shape
For i = 0 To UBound(Labels)
For Each s In ActivePage.Shapes(DeviceObjectName).Shapes
If s.Name = Labels(i) Then
'MsgBox GetShapeData(s, "Category")
Dim dynLabel As Control
Dim dynTextBox As Control
Dim dynXferLabelButton As Control
If InStr(Labels(i), "In") > 0 Then
cIn = cIn + 1
ctrlLeft = 20
ctrlTop = (20 * cIn)
Set dynLabel = frameInputs.Controls.Add("Forms.Label.1", "dynLabel" & CStr(s.Index), True)
Set dynTextBox = frameInputs.Controls.Add("Forms.TextBox.1", "dynTextBox" & CStr(s.Index), True)
Set dynXferLabelButton = frameInputs.Controls.Add("Forms.CommandButton.1", "dynXferLabelButton" & CStr(s.Index), True)
Else:
cOut = cOut + 1
ctrlLeft = 20
ctrlTop = (20 * cOut)
Set dynLabel = frameOutputs.Controls.Add("Forms.Label.1", "dynLabel" & CStr(s.Index), True)
Set dynTextBox = frameOutputs.Controls.Add("Forms.TextBox.1", "dynTextBox" & CStr(s.Index), True)
Set dynXferLabelButton = frameOutputs.Controls.Add("Forms.CommandButton.1", "dynXferLabelButton" & CStr(s.Index), True)
End If
ctrlText = s.Text
If LabelDataValue(s.Index) <> "" Then ctrlText = LabelDataValue(s.Index)
With dynLabel
.Top = ctrlTop
.Left = ctrlLeft
.Caption = ctrlText
.Tag = s.Index
End With
If GetShapeData(s, "Label") = 0 Then
ctrlText = s.Text
Else:
ctrlText = GetShapeData(s, "Label")
End If
With dynTextBox
.Top = ctrlTop
.Left = ctrlLeft + 80
.Text = ctrlText
.Tag = s.Index
End With
With dynXferLabelButton
.Top = ctrlTop
.Left = ctrlLeft + 60
.Caption = ">>"
.Width = 20
.Height = 17
.FONTSIZE = 6
.Tag = s.Index
End With
ReDim Preserve cmdArray(i)
Set cmdArray(i).cmdEvents = dynXferLabelButton
Exit For
End If
Next
Next i
Dim totalLines As Integer
If cIn >= cOut Then
totalLines = cIn
Else:
totalLines = cOut
End If
Me.Height = (25 * totalLines) + 150
frameInputs.Height = (25 * totalLines)
frameOutputs.Height = (25 * totalLines)
If Me.Height < 330 Then Me.Height = 330
cmdCancel.Top = Me.Height - 60
cmdSetDevice.Top = Me.Height - 60
Dim c As Control
For Each c In Me.frameInputs.Controls
MsgBox c.Name
Next
End Sub
Class 按钮事件
Public WithEvents cmdEvents As MSForms.CommandButton
Private Sub cmdEvents_Click()
MsgBox cmdEvents.Tag
MsgBox frmSetDevice.frameInputs.Controls.Item("dynLabel" & cmdEvents.Tag).Caption
Dim c As Control
For Each c In frmSetDevice.frameInputs.Controls
MsgBox c.Name
Next
End Sub
frmSetDevice
指的是“基本”用户窗体,而不是显示的实例。
如果您在 class 中为相应的 Label 和 Textbox 对象添加字段,那么您可以在 Click 事件中使用它们,而不必按名称查找它们
Public WithEvents cmdEvents As MSForms.CommandButton
Public lbl As MSForms.Label 'populate these when you populate cmdEvents
Public txt As MSForms.Textbox
Private Sub cmdEvents_Click()
MsgBox lbl.Caption 'etc etc
End Sub
我喜欢使用全局集合来保存这些类型的事件处理对象:您可以 .Add
而无需保持计数和调整数组大小。