创建 MyOSPObject 类

在前面的主题中,我们添加了一个 ActiveX DLL 工程到 AXData 示例中。在这个步骤中,我们将创建一个实现 OLE DB Simple Provider (OSP) 接口的类来访问保存在一个文本文件中的数据。

注意 该主题是帮助您创建示例数据源系列主题的一部分。创建数据源是第一部分。

要创建 MyOSPObject 类,请按照以下步骤执行:

  1. 在“工程资源管理器”中,从 MyDataComponent 工程中选择 Class1。在“属性”窗口中,设置 Class1 的属性如下:
    属性 设置值
    (Name) MyOSPObject

    您可能已经注意到 DataSourceBehavior 被设置为 none。如果这个部件将作为一个数据源的话,难道 DataSourceBehavior 不应该设置为另外的值吗?不要担心,在后面的步骤中我们将添加另一个类,这个类为该部件提供了数据源能力。

  2. 在“工程资源管理器”中双击“MyOSPObject”打开它的代码窗口。

  3. 在“对象窗口”框中,选择(“通用”)。在“过程”框中,选择(“Declarations”),定位到代码模块的顶部。添加下面的代码:
    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 类的所有接口。

  4. 添加下面的代码过程,以便从一个文件中读、写数据:
    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
    
  5. 在“对象窗口”框中,选择“类”。在“过程”框中,选择 Terminate 事件。添加下面的代码到 Class_Terminate 事件过程,当该类被终止时保存数据:
    Private Sub Class_Terminate()
    On Error Resume Next
    '调用 SaveData 方法
    SaveData
    End Sub
    

要实现 OLEDBSimpleProvider,请按照以下步骤执行:

因为 MyOSPObject 类实现 OLEDBSimpleProvider 类,即使不使用它的所有接口,我们也必须实现它们:

  1. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 addOLEDBSimpleProviderListener 函数。添加下面的代码到函数过程,以便添加当数据更改时通知该类的 listener:
    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
    
  2. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 deleteRows 函数。添加下面的代码到该过程,以便从一个文件中删除一行数据:
    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)
    
    ' 通知每一个 listenerFor 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
    
  3. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 find 函数。添加下面代码到该过程,以便查找一个文件中的数据:
    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
    
  4. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 getColumnCount 函数。添加下面的代码到该过程,以便返回一个文件中列的数目:
    Private Function OLEDBSimpleProvider_getColumnCount() As Long
    OLEDBSimpleProvider_getColumnCount = ColCount
    End Function
    
  5. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 getEstimatedRows 函数。添加下面的代码到该过程,以便返回一个文件中估计的数据行数目:
    Private Function OLEDBSimpleProvider_getEstimatedRows() As Long
    OLEDBSimpleProvider_getEstimatedRows = RowCount
    End Function
    
  6. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 getLocale 函数。添加下面的代码到该过程:
    Private Function OLEDBSimpleProvider_getLocale() As String
    OLEDBSimpleProvider_getLocale = ""
    End Function
    

    注意在这种情况下,该函数只返回一个 null 值。即使它没有做任何工作,也必须添加该函数,因为这个类实现 OLEDBSimpleProvider,必须包括它的所有接口。

  7. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 getRowCount 函数。添加下面的代码到该过程,以便返回一个文件中数据行的数目:
    Private Function OLEDBSimpleProvider_getRowCount() As Long
    OLEDBSimpleProvider_getEstimatedRows = RowCount
    End Function
    
  8. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 getRWStatus 函数。添加下面的代码到该过程,以便设置列的 Read/Write 状态,在此情况下,第一列是只读的,而其余的列是可读写的:
    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
    
  9. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 getVariant 函数。添加下面的代码到该过程,以便返回保存在指定行和列中的数据:
    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 函数也接受一个格式变量,该变量用于确定返回数据的格式。

  10. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 insertRows 函数。添加下面的代码到该过程,以便插入一个新数据行到文件中:
    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
    
  11. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 isAsynch 函数。添加下面的代码到该过程,以便确定 OSP 是否可以异步地返回数据:
    Private Function OLEDBSimpleProvider_isAsync() As Long
    OLEDBSimpleProvider_isAsync = False
    End Function
    
  12. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 removeOLEDBSimpleProviderListener 函数。添加下面的代码到该过程,以便删除一个 listener:
    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
    
  13. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 setVariant 函数。添加下面的代码到该过程,以便从一个指定的行和列中检索数据,并指定一个 listener 提供数据已经更改的通知:
    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
    
  14. 在“对象窗口”框中,选择“OLEDBSimpleProvider”。在“过程”框中,选择 stopTransfer 函数。添加下面的代码到该过程:
    Private Sub OLEDBSimpleProvider_stopTransfer()
    '不做任何工作,因为已经充填完毕
    End Sub
    

    注意在这个过程中没有任何代码,但是必须包括这个过程,因为该类实现 OLEDBSimpleProvider。您可以在这里添加代码,该代码允许您在一个长时间传送期间取消加载。

  15. 从“文件”菜单中选择“保存工程组”保存您的更改。当提示输入 Class 模块的一个文件名时,选择缺省值(MyOSPObject.cls)。当提示输入工程的一个文件名时,选择缺省值(MyDataComponent.vbp)。

即使看上去好象代码很多,但它的理由也很充分,MyOSPObject 类提供了几乎在一个数据库中能够找到的所有功能。通过 OSP,在过去使用一个数据库的地方您可以使用几乎所有的文件。

在下一个步骤中,我们将创建另一个类,该类将作为到 MyOSPObject 类的数据源。

步骤

该主题是帮助您创建示例 ActiveX 数据源系列主题的一部分。

请参阅
到下一步骤 创建 MyDataSource 类
从头开始 创建数据源