在前面的主题中,我们添加了一个 ActiveX DLL 工程到 AXData 示例中。在这个步骤中,我们将创建一个实现 OLE DB Simple Provider (OSP) 接口的类来访问保存在一个文本文件中的数据。
注意 该主题是帮助您创建示例数据源系列主题的一部分。创建数据源是第一部分。
要创建 MyOSPObject 类,请按照以下步骤执行:
属性 | 设置值 |
(Name) | MyOSPObject |
您可能已经注意到 DataSourceBehavior 被设置为 none。如果这个部件将作为一个数据源的话,难道 DataSourceBehavior 不应该设置为另外的值吗?不要担心,在后面的步骤中我们将添加另一个类,这个类为该部件提供了数据源能力。
Option Explicit
Implements OLEDBSimpleProvider
Dim MyOSPArray()
Dim RowCount As Integer
Dim ColCount As Integer
Dim colListeners As New Collection
Dim ospl As OLEDBSimpleProviderListener
Public FilePath As String
注意 OLEDBSimpleProvider 的 Implements 关键字的使用。记住,Implements 象一个合约,它意味着您需要实现 OLEDBSimpleProvider 类的所有接口。
Public Sub LoadData() '
这个过程从一个分号隔离的文件加载数据
'
到一个数组。Dim GetLine As Variant
Dim Spot As Integer, Position As Integer
Dim Row As Integer, Col As Integer
On Error GoTo ErrorTrap
Open FilePath For Input Lock Read Write As #1
Position = 1
Row = 0
Line Input #1, GetLine
Spot = InStr(1, GetLine, ";")
RowCount = val(Left$(GetLine, Spot))
ColCount = val(Right$(GetLine, Len(GetLine) - Spot))
ReDim MyOSPArray(RowCount + 1, ColCount + 1)
While Not EOF(1)
Line Input #1, GetLine
Col = 1
Spot = InStr(1, GetLine, ";")
While Spot <> 0
MyOSPArray(Row, Col) = Left$(GetLine, Spot - 1)
Col = Col + 1
GetLine = Right$(GetLine, Len(GetLine) - Spot)
Spot = InStr(1, GetLine, ";")
Wend
If Len(GetLine) <> 0 Then
MyOSPArray(Row, Col) = GetLine
End If
Row = Row + 1
Wend
Close #1
Exit Sub
ErrorTrap:
Err.Raise (E_FAIL)
End Sub
Public Sub SaveData()
'
这个过程将一个数组中的数据写到一个分号隔离'
的文件中。Dim PutLine As Variant
Dim iRow As Integer, iCol As Integer
On Error GoTo ErrorTrap
Open FilePath For Output Lock Read Write As #1
Print #1, RowCount & ";" & ColCount
For iRow = 0 To RowCount
For iCol = 1 To ColCount
PutLine = PutLine & MyOSPArray(iRow, iCol) & ";"
Next iCol
Print #1, PutLine
PutLine = ""
Next iRow
Close #1
Exit Sub
ErrorTrap:
Err.Raise (E_FAIL)
End Sub
Private Sub Class_Terminate() On Error Resume Next '
调用SaveData
方法SaveData
End Sub
要实现 OLEDBSimpleProvider,请按照以下步骤执行:
因为 MyOSPObject 类实现 OLEDBSimpleProvider 类,即使不使用它的所有接口,我们也必须实现它们:
Private Sub OLEDBSimpleProvider_addOLEDBSimpleProviderListener _ (ByVal pospIListener As OLEDBSimpleProviderListener) '
添加一个listener
到Listeners
集合。If Not (pospIListener Is Nothing) Then
Set ospl = pospIListener
colListeners.Add ospl
End If
End Sub
Private Function OLEDBSimpleProvider_deleteRows _ (ByVal iRow As Long, ByVal cRows As Long) As Long Dim TempArray() Dim listener As OLEDBSimpleProviderListener Dim v As Variant '
确保iRow
是在正确的范围:If iRow < 1 Or iRow > RowCount Then
Err.Raise (E_FAIL)
End If
'
设置cRows
为可以删除的实际数目If iRow + cRows > RowCount + 1 Then
cRows = RowCount - iRow + 1
End If
'
建立一个临时数组cNewRows = RowCount - cRows
ReDim TempArray(cNewRows + 1, ColCount + 1)
'
通知每一个listener
:For Each v In colListeners
Set listener = v
listener.aboutToDeleteRows iRow, cRows
Next
'
复制删除行前面的行For Row = 0 To iRow - 1
For Col = 0 To ColCount
TempArray(Row, Col) = MyOSPArray(Row, Col)
Next Col
Next Row
'
复制删除行后面的行For Row = iRow + cRows To RowCount
For Col = 0 To ColCount
TempArray(Row - cRows, Col) = MyOSPArray(Row, Col)
Next Col
Next Row
'
重新分配要复制到的目标数组ReDim MyOSPArray(cNewRows + 1, ColCount + 1)
'
重新设置回实际的行计数RowCount = cNewRows
'
复制这些行For Row = 0 To cNewRows
For Col = 0 To ColCount
MyOSPArray(Row, Col) = TempArray(Row, Col)
Next Col
Next Row
'
清除临时数组ReDim TempArray(0)
'
通知每一个listener
For Each v In colListeners
Set listener = v
listener.deletedRows iRow, cRows
Next
'
返回删除行的数目OLEDBSimpleProvider_deleteRows = cRows
End Function
Private Function OLEDBSimpleProvider_find(ByVal iRowStart As Long, _ ByVal iColumn As Long, ByVal val As Variant, _ ByVal findFlags As OSPFIND, ByVal compType As OSPCOMP) As Long Dim RowStart As Integer, RowStop As Integer If (findFlags And (OSPFIND_UP Or OSPFIND_UPCASESENSITIVE)) _ <> 0 Then RowStart = RowCount + 1 RowStop = 0 StepValue = -1 Else RowStart = 0 RowStop = RowCount + 1 StepValue = 1 End If If (findFlags And (OSPFIND_CASESENSITIVE Or _ OSPFIND_UPCASESENSITIVE)) <> 0 Then CaseSens = 1 '
使用区分大小写的文本比较Else
CaseSens = 0 '
不区分大小写,使用二进制比较End If
If VarType(val) = vbString Then
StringComp = True
Else
StringComp = False
End If
iAnswerRow = -1
For iRow = RowStart To RowStop Step StepValue
If StringComp Then
CompResult = StrComp(MyOSPArray(iRow, iColumn), _
val, CaseSens)
Select Case (compType)
Case OSPCOMP_DEFAULT, OSPCOMP_EQ:
If CompResult = 0 Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_GE
If CompResult >= 0 Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_GT
If CompResult > 0 Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_LE
If CompResult <= 0 Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_LT
If CompResult < 0 Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_NE
If CompResult <> 0 Then
iAnswerRow = iRow
Exit For
End If
End Select
Else
Select Case (compType)
Case OSPCOMP_DEFAULT, OSPCOMP_EQ:
If MyOSPArray(iRow, iColumn) = val Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_GE
If MyOSPArray(iRow, iColumn) >= val Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_GT
If MyOSPArray(iRow, iColumn) > val Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_LE
If MyOSPArray(iRow, iColumn) <= val Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_LT
If MyOSPArray(iRow, iColumn) < val Then
iAnswerRow = iRow
Exit For
End If
Case OSPCOMP_NE
If MyOSPArray(iRow, iColumn) <> val Then
iAnswerRow = iRow
Exit For
End If
End Select
End If
Next iRow
OLEDBSimpleProvider_find = iAnswerRow
End Function
Private Function OLEDBSimpleProvider_getColumnCount() As Long
OLEDBSimpleProvider_getColumnCount = ColCount
End Function
Private Function OLEDBSimpleProvider_getEstimatedRows() As Long
OLEDBSimpleProvider_getEstimatedRows = RowCount
End Function
Private Function OLEDBSimpleProvider_getLocale() As String
OLEDBSimpleProvider_getLocale = ""
End Function
注意在这种情况下,该函数只返回一个 null 值。即使它没有做任何工作,也必须添加该函数,因为这个类实现 OLEDBSimpleProvider,必须包括它的所有接口。
Private Function OLEDBSimpleProvider_getRowCount() As Long
OLEDBSimpleProvider_getEstimatedRows = RowCount
End Function
Private Function OLEDBSimpleProvider_getRWStatus _ (ByVal iRow As Long, ByVal iColumn As Long) As OSPRW If iColumn = 1 Then '
使第一列只读OLEDBSimpleProvider_getRWStatus = OSPRW_READONLY
Else
'
使该列可读写OLEDBSimpleProvider_getRWStatus = OSPRW_READWRITE
End If
End Function
Private Function OLEDBSimpleProvider_getVariant _
(ByVal iRow As Long, ByVal iColumn As Long, _
ByVal format As OSPFORMAT) As Variant
OLEDBSimpleProvider_getVariant = MyOSPArray(iRow, iColumn)
End Function
GetVariant 函数也接受一个格式变量,该变量用于确定返回数据的格式。
Private Function OLEDBSimpleProvider_insertRows _ (ByVal iRow As Long, ByVal cRows As Long) As Long Dim TempArray() Dim listener As OLEDBSimpleProviderListener Dim v As Variant '
建立一个临时数组cNewRows = RowCount + cRows
ReDim TempArray(cNewRows + 1, ColCount + 1)
'
如果插入超过数组的结尾,则插入到'
数组的结尾If iRow > RowCount Then
iRow = RowCount + 1
End If
'
通知listener
For Each v In colListeners
Set listener = v
listener.aboutToInsertRows iRow, cRows
Next
'
复制现存的行For Row = 0 To iRow
For Col = 0 To ColCount
TempArray(Row, Col) = MyOSPArray(Row, Col)
Next Col
Next Row
'
复制插入行后面的行For Row = iRow + 1 + cRows To cNewRows
For Col = 0 To ColCount
TempArray(Row, Col) = MyOSPArray(Row - cRows, Col)
Next Col
Next Row
'
重新分配复制到的目标数组ReDim MyOSPArray(cNewRows + 1, ColCount + 1)
'
复制这些行For Row = 0 To cNewRows
For Col = 0 To ColCount
MyOSPArray(Row, Col) = TempArray(Row, Col)
Next Col
Next Row
'
清除临时数组ReDim TempArray(0)
'
重新设置回实际行的计数RowCount = cNewRows
'
通知listener
For Each v In colListeners
Set listener = v
listener.insertedRows iRow, cRows
Next
'
返回插入的行数OLEDBSimpleProvider_insertRows = cRows
End Function
Private Function OLEDBSimpleProvider_isAsync() As Long
OLEDBSimpleProvider_isAsync = False
End Function
Private Sub OLEDBSimpleProvider_removeOLEDBSimpleProviderListener _ (ByVal pospIListener As OLEDBSimpleProviderListener) '
删除此listener
For i = 1 To colListeners.Count
If colListeners(i) Is pospIListener Then
colListeners.Remove i
End If
Next
End Sub
Private Sub OLEDBSimpleProvider_setVariant(ByVal iRow As Long, _
ByVal iColumn As Long, ByVal format As OSPFORMAT, _
ByVal Var As Variant)
Dim listener As OLEDBSimpleProviderListener
Dim v As Variant
For Each v In colListeners
Set listener = v
listener.aboutToChangeCell iRow, iColumn ' Pre-notification
Next
MyOSPArray(iRow, iColumn) = Var
For Each v In colListeners
Set listener = v
listener.cellChanged iRow, iColumn ' Post-notification
Next
End Sub
Private Sub OLEDBSimpleProvider_stopTransfer() '
不做任何工作,因为已经充填完毕End Sub
注意在这个过程中没有任何代码,但是必须包括这个过程,因为该类实现 OLEDBSimpleProvider。您可以在这里添加代码,该代码允许您在一个长时间传送期间取消加载。
即使看上去好象代码很多,但它的理由也很充分,MyOSPObject 类提供了几乎在一个数据库中能够找到的所有功能。通过 OSP,在过去使用一个数据库的地方您可以使用几乎所有的文件。
在下一个步骤中,我们将创建另一个类,该类将作为到 MyOSPObject 类的数据源。
该主题是帮助您创建示例 ActiveX 数据源系列主题的一部分。
要 | 请参阅 |
到下一步骤 | 创建 MyDataSource 类 |
从头开始 | 创建数据源 |