您好,VBA社区, 我对vba还是很陌生,正在尝试学习很多东西。预先感谢您浏览我的代码和对所面临问题的描述。
我在页面上有一个按钮,用于调用新的用户表单。
代码片段1:
Sub btnShowDetails_Click()
Call frmShowDeets.ShowDeets
End Sub
...,它将调用“ frmShowDeets”用户窗体中的下一段代码:
代码片段2:
Public Sub ShowDeets()
Dim frm As frmShowDeets
Set frm = New frmShowDeets 'this line triggers the Userform_Initialize() event below
frm.Show
End Sub
...触发:
代码片段3:
Private Sub UserForm_Initialize()
Dim comboBoxItem As Range
For Each comboBoxItem In ContactList.Range("tblContactList[CompanyName]")
'^refers to unique values in a named range
With Me.boxCompanySelection
.AddItem comboBoxItem.Value
End With
Next comboBoxItem
End Sub
因此,在这一点上,我要显示的表单在其一个组合框中加载了供用户选择的值。用户选择一家公司,然后Combobox_Change事件触发其他例程,为该公司提取信息。
代码片段4:
Public Sub boxCompanySelection_Change()
Call frmShowDeets.PullData
End Sub
Sub PullData()
Dim numCompanies As Long
numCompanies = ContactList.Range("B6").Value 'this holds a count of the rows in the named range
Dim FoundCell As Range
Set FoundCell = ContactList.Range("tblContactList[Company Name]").Find(What:=boxCompanySelection.Text, LookIn:=xlValues, LookAt:=xlWhole)
Dim CompanyRow As Long
CompanyRow = FoundCell.Row
With ContactList
'pull a bunch of the company's details
End With
End Sub
这是很奇怪的地方...一旦显示了表单,并且用户选择了一个组合框项目,则触发Combobox_Change事件,代码中断,因为Range()的'What:= boxCompanySelection.Text'部分).Find方法读取为“”为空(即使代码段3旨在加载公司名称,并且代码段4仅在用户从组合框中选择这些公司名称之一时才触发),并且我不需要构建处理“未找到”异常的方法,因为唯一可能的值应该是从我命名的范围中拉入的值。
通过逐步执行代码,我确定由于某些原因,代码段2和3在运行代码段4之前先运行 TWICE 。有谁知道我的代码导致这种情况发生?我认为显示和加载组合框值的表单与从中读取数据的任何代码片段4之间都存在脱节。
奇怪的是,如果我从代码片段2开始运行代码(忽略代码片段1中的按钮调用),那么该表格将按预期工作,并且据我所知2和3仅运行一次。
这个问题可能是我所忽略的简单问题,但我只是无法弄清楚它是什么。再次感谢!
答案 0 :(得分:3)
您必须了解表单是一个对象-与其他任何类模块完全一样,除了表单恰巧具有设计器和基类,因此UserForm1
继承了成员UserForm
类中的一个。
表单也有一个默认实例,许多教程很高兴地跳过了这一非常重要但相当技术性的方面,这使我们正好进入了Stack Overflow,并涉及到涉及全局状态的错误。意外存储在默认实例上。
Call frmShowDeets.ShowDeets
假设frmShowDeets
是表单类的名称,并假定这是对要运行的表单的第一个引用,则是默认实例的UserForm_Initialize
处理程序 在.
点运算符执行并取消引用该对象时运行。 然后 ShowDeets
方法运行。
Public Sub ShowDeets()
Dim frm As frmShowDeets
Set frm = New frmShowDeets 'this line triggers the Userform_Initialize() event below
frm.Show
End Sub
该行在名为UserForm_Initialize
的本地实例上触发frm
-这是一个完全独立的同一类的对象。 Initialize
处理程序只要正确地初始化了类的实例即创建即运行。 Terminate
处理程序在实例被销毁时运行。
因此ShowDeets
充当某种“工厂方法”,可以创建并显示frmShowDeets
类/表单的新实例-换句话说,默认实例上发生的任何事情都与之无关点:您正在使用的对象位于ShowDeets
范围内,被命名为frm
,并且超出范围就会被销毁。
完全删除ShowDeets
方法。替换为:
Call frmShowDeets.ShowDeets
与此:
With New frmShowDeets
.Show
End With
现在Initialize
处理程序不再在默认实例上运行。
您想要的是完全避免使用默认实例。将表单代码后的所有frmShowDeets
替换为Me
(请参阅Understanding 'Me' (no flowers, no bees)),这样就不会在默认实例中意外存储任何状态。
Call frmShowDeets.PullData
变得简单:
Call Me.PullData
甚至:
PullData
因为Call
在任何地方都不需要,并且在类模块的代码中进行成员调用时,Me
限定符始终是隐式的。