我过去曾写过处理TableDef和Field属性的代码。这并不难,它只是涉及到在Field.Properties集合中循环,并沿途进行一定量的错误检查。Property.Name、Property.Type和Property.Value产生了我们需要的一切。
问题是:我刚刚写了一些代码来为Form和Report控件做同样的属性枚举。属性名和值都很好,但.Type不正确。举个例子。
Public Sub TestPropTypes()
Dim dbs As DAO.Database
Set dbs = CurrentDb()
Dim td As TableDef
Dim fld As DAO.Field
Set td = dbs.TableDefs("CalendarEvent_")
Set fld = td.Fields("EventDescription")
PrintObjectProps fld
Dim f As Form
Dim c As Control
DoCmd.OpenForm "fmCalendarDates", acDesign
Set f = Forms!fmCalendarDates
Set c = f.Controls("Label7")
PrintObjectProps c
End Sub
Private Sub PrintObjectProps(c As Object, Optional RecursionDepth As Integer = 0)
Dim ExistingProperty As DAO.Property
Dim PropCount As Integer
Dim GotValue As Boolean
Dim v As Variant
Debug.Print c.Name
For Each ExistingProperty In c.Properties
If PropCount > 12 Then Exit Sub
GotValue = True
On Error Resume Next
v = ExistingProperty.Value
If Err.number <> 0 Then GotValue = False
On Error GoTo 0
If GotValue Then
Debug.Print " " & ExistingProperty.Name & " " _
& GetFieldDDLTypeName(ExistingProperty.Type) & "(" & ExistingProperty.Type & ") " _
& xf.Gen.dq(CStr(ExistingProperty.Value))
End If
PropCount = PropCount + 1
Next
End Sub
Public Function GetFieldDDLTypeName(FieldType As DAO.DataTypeEnum) As String
Dim rtnStr As String
' as per: http://allenbrowne.com/ser-49.html
Select Case FieldType
Case dbBoolean: rtnStr = "YESNO"
Case dbByte: rtnStr = "BYTE"
Case dbInteger: rtnStr = "SHORT"
Case dbLong: rtnStr = "LONG"
Case dbCurrency: rtnStr = "CURRENCY"
Case dbSingle: rtnStr = "SINGLE"
Case dbDouble: rtnStr = "DOUBLE"
Case dbDate: rtnStr = "DATETIME"
Case dbBinary: rtnStr = "BINARY"
Case dbText: rtnStr = "TEXT"
Case dbLongBinary: rtnStr = "LONGBINARY"
Case dbMemo: rtnStr = "MEMO"
Case DBGuid: rtnStr = "GUID"
End Select
GetFieldDDLTypeName = rtnStr
End Function
Yields:
TestPropTypes
EventDescription
Attributes LONG(4) "2"
CollatingOrder SHORT(3) "1033"
Type SHORT(3) "10"
Name MEMO(12) "EventDescription"
OrdinalPosition SHORT(3) "2"
Size LONG(4) "100"
SourceField MEMO(12) "EventDescription"
SourceTable MEMO(12) "CalendarEvent_"
DataUpdatable YESNO(1) "False"
DefaultValue MEMO(12) ""
Label7
EventProcPrefix DATETIME(8) "Label7"
Name DATETIME(8) "Label7"
ControlType BYTE(2) "100"
Caption DATETIME(8) "Description"
Visible LONGBINARY(11) "True"
Width BYTE(2) "1875"
Height BYTE(2) "285"
Top BYTE(2) "425"
Left BYTE(2) "1048"
BackStyle BYTE(2) "0"
BackColor SHORT(3) "16777215"
BorderStyle BYTE(2) "0"
OldBorderStyle BYTE(2) "0"
所以你可以看到第一组,来自TableDef中的一个Field,产生了我们所期望的结果。
第二组,使用完全相同的渲染代码,来自表单中的一个控件。所有的MEMO属性都以DATETIME的形式出现,所有的YESNO都以LONGBINARY的形式出现,等等。至少是一致的,我大概可以推导出类型转换,并写出转换算法。我一开始觉得可能是用了ADO值(因为YESNO是11),但是和其他ADO值没有一致性,而且我们还明确使用了DAO对象,这样就说不通了。
这是不一致的,我想找出并记录一些合理的解决方案.以前有人遇到过这种情况吗?
在Access 2003和2007上测试过,所以我确信这不是我的Access版本中的一个奇怪的错误。这是一个Access 2003格式的数据库,所以没有任何扩展类型在工作中造成影响。
EDIT:问题解决了,感谢HansUp。现在我们要检查一下tablefield与formreport属性是否确实使用了不同的返回值。我提供了这个。
Public Sub TestPropTypes1()
Dim prop As DAO.Property
Dim dbs As DAO.Database
Set dbs = CurrentDb()
DoCmd.OpenForm "fmCalendarDates", acDesign
Debug.Print "Property", "Value", "varType(.Value)", ".Type"
Debug.Print
Set prop = dbs.TableDefs("CalendarEvent_").Fields("EventDate").Properties("Name")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
Set prop = dbs.TableDefs("CalendarEvent_").Fields("EventDate").Properties("Required")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
Set prop = Forms!fmCalendarDates!Label7.Properties("Name")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
Set prop = Forms!fmCalendarDates!Label7.Properties("Visible")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
End Sub
返回值
TestPropTypes1
Property Value vartype(.Value) .Type
Name EventDate 8 12
Required False 11 1
Name Label7 8 8
Visible True 11 11
这将强烈地表明,TableDef字段属性确实使用了DAO.DataTypeEnum类型,而表单属性似乎产生了VBA.VbVarType的返回。例如,字段的Required属性返回的类型对应于VbVarType的NULL,而它是DataTypeEnum的YesNo。
请注意prop.Type和varType(prop.Value)之间的微妙区别。
虽然我们可以使用varType(prop.value),但这通常会被认为是不好的做法,因为类型可能取决于值的内容(例如,一个Null),而.Type是权威的元数据。在系统属性这样的情况下,值可能表现良好,可能没有实际区别。
真正让人惊讶的是,参考资料中根本没有提到这个问题。
使用 VBA.VbVarType
枚举 Type
.
您的代码将一个属性的 Type
身为 DAO.DataTypeEnum
. 这将导致代码翻译成 Type
不正确。
其实这不是一个问题,它只出现在表单和报表控件上。 你的表的字段属性也有同样的问题。 例如,输出样本错误地识别了字段的 Name
属性为备忘录类型。
Name MEMO(12) "EventDescription"
但是一个字段的 Name
属性是一个变体,其子类型是字符串。
? CurrentDb.TableDefs("tblFoo").Fields("long_text").Properties("Name").Type
12
? VBA.VbVarType.vbVariant
12
' this is the WRONG translation ...
? DAO.DataTypeEnum.dbMemo
12
如果你的目标是翻译属性的 Type
到人性化的文本,考虑 TypeName()
函数。
? TypeName(CurrentDb.TableDefs("tblFoo").Fields("long_text").Properties("Name").Value)
String
如果这个建议不能被接受,你可以创建一个自定义函数来翻译。 Type
到您想要的文本。 然而,翻译 Type
到DDL字段数据类型名称的方法是错误的,我认为。
感谢所有这些好的信息.我正在研究这个问题。在Access2019上工作了一些小时后,我可以添加一些提示.我将函数GetFieldDDLTypeName()改为GetFieldVBATypeName(),如下所示,这解决了TypeNames不一致的问题,也解决了复合数组类型。
虽然应用在control.properties上,它显示出一些不一致的地方。
Control.Property Type TypeName Value
1-OLEBound.VarOleObject 6 Currency 65
2-ToggleButton.InSelection 11 Boolean NULL
3-Image.InSelection 11 Boolean 6375
Public Function GetVBATypeName(FieldType As VBA.VbVarType) As String
Dim rtnStr As String
If FieldType > 8192 Then
rtnStr = GetVBATypeName2(8192) & "-" & GetVBATypeName2(FieldType - 8192)
Else
rtnStr = GetVBATypeName2(FieldType)
End If
GetVBATypeName = rtnStr
End Function
Public Function GetVBATypeName2(FieldType As VBA.VbVarType) As String
Dim rtnStr As String
Select Case FieldType
Case vbEmpty: rtnStr = "EMPTY"
Case vbNull: rtnStr = "NULL"
Case vbInteger: rtnStr = "INTEGER"
Case vbLong: rtnStr = "LONG"
Case vbSingle: rtnStr = "SINGLE"
Case vbDouble: rtnStr = "DOUBLE"
Case vbCurrency: rtnStr = "CURRENCY"
Case vbDate: rtnStr = "DATE"
Case vbString: rtnStr = "STRING"
Case vbObject: rtnStr = "OBJECT"
Case vbError: rtnStr = "ERROR"
Case vbBoolean: rtnStr = "BOOLEAN"
Case vbVariant: rtnStr = "VARIANT"
Case vbDataObject: rtnStr = "DATAOBJECT"
Case vbDecimal: rtnStr = "DECIMAL"
Case vbByte: rtnStr = "BYTE"
Case 20: rtnStr = "LONGLONG" 'vbLongLong ...
Case vbUserDefinedType: rtnStr = "USERDEFINED"
Case vbArray: rtnStr = "ARRAY"
Case Else: rtnStr = "Error DataType"
End Select
GetVBATypeName2 = rtnStr
End Function
要注意的是,Case 20,对应于vbLongLong,但不被VBA识别'Case vbLongLong: rtnStr = "LONGLONG" '(=20)这里描述了 。 https:/docs.microsoft.comen-usofficevbalanguagereferenceuser-interface-helpvartype-function#return-values。.如果你有任何更正,请在这里发帖,为更好的软件做贡献。