MS Access是否允许获取表单的记录源值而无需打开表单本身?我现在正在尝试优化我的代码,我做的是我只是隐藏表单然后获取Recordsource表单查询但是加载需要时间,因为某些表单在onload上触发代码。
答案 0 :(得分:2)
一种选择是将表单的记录源保存为查询。假设您有一个名为[AgentForm]的表单,其记录源为
SELECT ID, AgentName FROM Agents
在开发数据库的.accdb副本中,在“设计视图”中打开表单,然后在“查询构建器”中打开“记录源”。点击“另存为”按钮...
并将查询保存为“AgentForm_RecordSource”。现在,表单的Record Source
属性只是对已保存查询的引用,并且可以通过QueryDef
对象直接访问查询本身。因此,您可以使用
Dim cdb As DAO.Database, qdf As DAO.QueryDef, sql As String
Set cdb = CurrentDb
Set qdf = cdb.QueryDefs("AgentForm_RecordSource")
sql = qdf.SQL
或者您可以继续使用
打开RecordsetDim cdb As DAO.Database, qdf As DAO.QueryDef, rst As DAO.Recordset
Set cdb = CurrentDb
Set qdf = cdb.QueryDefs("AgentForm_RecordSource")
Set rst = qdf.OpenRecordset
答案 1 :(得分:2)
我这里的游戏迟到了 - 我有时会在原始问题发布后数月或数年发布答案,因为当我快速搜索'Stack'找到与我自己当天的问题相关的问题时,我会发布自己的解决方案,但没有我可以实际使用的答案。
[更新,2016年6月6日]
从Access 2010开始,“NameMap”属性在文档对象中不可用。但是,'Stacker Thunderframe已经指出现在可以在'MsysNameMap'表中找到它。
我修改了代码,这在Access 2010和2013中有效。
[/ UPDATE]
表单的大部分属性仅在表单打开时可用,但有些表单可在DAO Documents集合中的表单条目中找到。
DAO'文档'是一个可怕的对象:它不会持久存在于内存中,每次使用它时都必须明确引用它:
FormName = "MyForm" For i = 0 To Application.CodeDb.Containers("Forms").Documents(FormName).Properties.Count - 1 Debug.Print i & vbTab & Application.CodeDb.Containers("Forms").Documents(FormName).Properties(i).Name & vbTab & vbTab & Application.CodeDb.Containers("Forms").Documents(FormName).Properties(i).Value Next
为您的表单运行该代码段,您将看到一个“NameMap”属性,其中包含表单控件的列表,以及表单属性的部分。
......以一种真正可怕的格式,需要二进制解析器。你可能想要在继续阅读之前停止阅读并服用阿司匹林。
如果NameMap的记录源的双字节二进制标签发生变化,或者它是特定于语言环境的,那么下面代码中的解决方案将停止工作。
这是一个可怕的黑客:我对你的理智的任何影响不承担任何责任。
好的,这是代码:
Private Function FormRecordSource_FromNameMap(FormName As String) As String
' Reads the Record Source from the NameMap Property of the Document object for the form.
' WARNING: there is a potential error here: if the form's RecordSource property is blank ' and it has one or more list controls with a .RecordSource property populating ' the list, this function will return the first list control's Record Source.
' This won't work if you're using non-ASCII characters (Char > 255) in your form name.
Dim i As Integer Dim j As Integer Dim k As Integer
Dim arrByte() As Byte
Dim strOut As String If Application.Version < 12 Then
arrByte = Application.CodeDb.Containers("Forms").Documents(FormName).Properties("NameMap").Value
For i = 1 To UBound(arrByte) - 2 Step 2
' 2-byte marker for a querydef in the NameMap:
If (arrByte(i) = 228 And arrByte(i + 1) = 64) Then
j = i + 2 Do While arrByte(j) = 0 And arrByte(j + 1) = 0 And j < UBound(arrByte) ' loop through the null chars between the marker and the start of the string j = j + 2 Loop
strOut = "" Do Until (arrByte(j) = 0 And arrByte(j + 1) = 0) Or j >= UBound(arrByte) - 2 If arrByte(j) = 0 Then j = j + 1 ' loop until we reach the null char which terminates this string ' appending the Bchars (not unicode Wchars!) of the table or query strOut = strOut & Chr(arrByte(j)) j = j + 2 Loop
Exit For ' we only want the first datasource End If
Next i
Else
arrByte = Nz(DLookup("[NameMap]", "[MSYSNameMap]", "[Name] = '" & FormName & "'"), vbNullChar)
If UBound(arrByte) < 4 Then Exit Function
strOut = "" For j = 60 To UBound(arrByte) - 2 Step 2
If arrByte(j) = 0 And arrByte(j + 1) = 0 Then Exit For
strOut = strOut & Chr(arrByte(j))
Next j
End If
frmRecordSource_FromNameMap = strOut
Erase arrByte
End Function
如果你在(比方说)OpenRecordset或DCOUNT函数中使用RecordSource,我建议你将它封装在方括号中:你可能会从RecordSource中的'SELECT'语句中获取隐藏查询对象的名称,该名称将包含需要特殊处理的“〜”代字符。
现在,你没有要求的额外的东西,但是其他人会在寻找他们在这里搜索'MS Access RecordSource for a closed form'的方式:
大部分时间,您的表单都会打开。问题是,你不知道......如果它是一个子表单,它可能在Forms()集合中不可见。更糟糕的是,作为子表单托管的表单可能作为多个打开表单中的多个实例存在。
祝你好运,如果你想要提取动态属性......就像过滤器一样,如果由VBA设置'动态',则记录源。
分享和享受:请接受我对代码示例中任何不需要的换行符的道歉。Public Function GetForm(FormName As String, Optional ParentName As String = "") As Form ' Returns a form object, if a form with a name like FormName is open ' FormName can include wildcards. ' Returns Nothing if no matching form is open.
' Enumerates subforms in open forms, and returns the subform .form object if ' it has a matching name. Note that a form may be open as multiple instances ' if more than one subform hosts it; the function returns the first matching ' instance. Specify the named parent form (or the subform control's name) if ' you need to avoid an error arising from multiple instances of the form.
Dim objForm As Access.Form
If ParentName = "" Then For Each objForm In Forms If objForm.Name Like FormName Then Set GetForm = objForm Exit Function End If Next End If
If GetForm Is Nothing Then For Each objForm In Forms Set GetForm = SearchSubForms(objForm, FormName, ParentName) If Not GetForm Is Nothing Then Exit For End If Next End If
End Function
Private Function SearchSubForms(objForm As Access.Form, SubFormName As String, Optional ParentName As String = "") As Form ' Returns a Form object with a name like SubFormName, if the named object SubFormName is subform ' of an open form , or can be recursively enumerated as the subform of an open subform.
' This function returns the first matching Form: note that a form can be instantiated in multiple ' instances if it is used by more than one subform control.
Dim objCtrl As Control For Each objCtrl In objForm
If TypeName(objCtrl) = "SubForm" Then If objCtrl.Form.Name Like SubFormName Then If ParentName = "" Or objForm.Name Like ParentName Or objCtrl.Name Like ParentName Then Set SearchSubForms = objCtrl.Form Exit For End If Else Set SearchSubForms = SearchSubForms(objCtrl.Form, SubFormName, ParentName) If Not SearchSubForms Is Nothing Then Exit For End If End If End If
Next objCtrl
End Function
Public Function FormRecordSource(FormName As String, Optional ParentName As String = "") As String ' Returns the Recordsource for a form, even if it isn't open in the Forms() collection
' This will look for open forms first. If you're looking for a subform, you may need a ' parent name for the form which hosts the subform: your named form might be open as a ' subform instance in more than one parent form.
' WARNING: there is a potential error here: if the form isn't open, and it has a blank ' RecordSource property, and it has one or more controls with a .RecordSource ' property populating a list, a list control's RecordSource could be returned
Dim objForm As Form
If FormName = "" Then Exit Function End If
Set objForm = GetForm(FormName, ParentName)
If objForm Is Nothing Then FormRecordSource = FormRecordSource_FromNameMap(FormName) Else FormRecordSource = objForm.RecordSource Set objForm = Nothing End If
End Function
答案 2 :(得分:1)
由于您无法在设计视图中打开表单并定期打开表单会导致性能问题,因此还有一些解决方法:
根据您要检查已关闭表单的记录源的方式,您可以在单独的模块中以下列方式设置全局变量:
Public glb_getrecordsource As String
之后,根据您调用代码的方式,您可以执行以下操作:
Private Sub Command1_Click()
glb_getrecordsource = "Yes"
DoCmd.OpenForm "Form1"
'... Do something
End Sub
然后,作为最后一步,将以下内容放在表单OnLoad事件的开头:
Private Sub Form_Load()
If glb_getrecordsource = "Yes" Then
glb_getrecordsource = Me.Form.RecordSource
DoCmd.Close acForm, "Form1", acSaveYes
Exit Sub
End If
'... Usual OnLoad events
End Sub
这至少可以解决性能问题,因为您不会在表单的加载事件中触发任何耗时的事件。
另一种解决方法: 您可以将表单导出为.txt文件,然后在文本文件中搜索记录源。以下代码将表单导出到指定文件夹中的.txt文件:
Dim db As Database
Dim d As Document
Dim c As Container
Dim sExportLocation As String
Set db = CurrentDb()
sExportLocation = "C:\AD\" 'Do not forget the closing back slash! ie: C:\Temp\
Set c = db.Containers("Forms")
For Each d In c.Documents
Application.SaveAsText acForm, d.Name, sExportLocation & "Form_" & d.Name & ".txt"
Next d
代码部分借鉴this论坛。之后,您只需打开文件并搜索记录源。如果recordsource为空,则不会导出,因此请记住这一点。另外,我怀疑这会改善性能,但谁知道呢!
答案 3 :(得分:1)
如果表单的记录源是SELECT
语句而不是表或保存的查询的名称,则可以检查QueryDefs
集合中隐藏的QueryDef
为其创建的Access记录来源声明。
如果存在,您可以检查其.SQL
属性。
strFormName = "Form15"
? CurrentDb.QueryDefs("~sq_f" & strFormName).SQL
SELECT DISTINCTROW *
FROM [DB Audits];
您可以捕获错误#3265,“此集合中找不到的项目”,如果QueryDef
不存在,将会抛出该错误。