vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
TOP-Angebot: 17 bzw. 24 Entwickler-Vollversionen zum unschlagbaren Preis!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Newsletter  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2017
 
zurück
Rubrik: Datenbanken   |   VB-Versionen: VB5, VB601.11.04
Recordset ohne ADO oder DAO - sozusagen virtuell

In vielen Applikationen wird immer wieder eine Datenstruktur benötigt um beispielsweise Programminformationen, oder aber vom Anwender angelegte Eingabedaten abzulegen. Dafür bietet sich natürlich eine Datenbank wie Access, MS-SQL oder MySQL Server an. Der Nachteil dieser Vorgehensweise ist, dass auf dem Zielsystem erst einmal diese Datenbank angelegt, bzw. installiert werden muss…

Autor:  Roland WutzkeBewertung:     [ Jetzt bewerten ]Views:  31.981 

In vielen Applikationen wird immer wieder eine Datenstruktur benötigt um beispielsweise Programminformationen, oder aber vom Anwender angelegte Eingabedaten abzulegen. Dafür bietet sich natürlich eine Datenbank wie Access, MS-SQL oder MySQL Server an. Der Nachteil dieser Vorgehensweise ist, dass auf dem Zielsystem erst einmal diese Datenbank angelegt, bzw. installiert werden muss. Weiterhin müssen Sie die nötigen ADO (MDAC) oder DAO Komponenten auf dem Zielsystem installieren. Das bringt oft Risiken mit sich, die Sie im Vorfeld nicht absehen können. Sicherlich kommen Sie um die Benutzung solcher Datenbanken nicht herum, wenn Sie mit komplexen Datenstrukturen arbeiten - doch was ist, wenn Sie lediglich mit einer Tabelle in Ihrer Applikation arbeiten?

Dieser Workshop zeigt Ihnen eine Möglichkeit auf, wie Sie mit einer Tabelle arbeiten können, ohne ADO oder DAO in Ihrem Projekt zu nutzen.

Die VB6 Klasse "clsRecordset" (virtuelles Recordset)

Grundlagen:
Ein ADO oder DAO Recordset ist im herkömmlichen Sinne eine Aneinanderreihung von Records. Ein Record wiederum beinhaltet die einzelnen Zellen (Fields), die die Informationen aufnehmen. Um diese Struktur mit den VB6 Bordmitteln abbilden zu können, benötigen wir eine Collection, die dem virtuellen Recordset zu Grunde liegt. Das Item dieser Collection, also unser Record selber, wird in einem eigenen Klassenmodul abgebildet.

Die Klasse "clsRecord"
Verwenden Sie diese Klasse um Ihre Datenstruktur (Record) zu definieren. Das nachfolgende Beispiel definiert einen Datensatz einer kleinen Kundenverwaltung:

' **************************************************************
' *
' * clsRecord - Klassenmodul
' *
' * Definieren Sie hier die Felder Ihres Records
' *
' **************************************************************
 
Option Explicit
 
Public Anrede As String
Public Vorname As String
Public Nachname As String
Public eMail As String
Public Betrag As Currency

Passen Sie diese Variablen an Ihre persönliche Struktur an.

Die Hauptklasse "clsRecordset"
Dieses VB6 Klassenmodul stellt nun alle Eigenschaften, Methoden und Ereignisse unseres virtuellen Recordsets zur Verfügung. Dabei orientiert sich das Recordset an einem ADODB.Recordset.

Die Eigenschaften der Klasse:

  • AbsolutePosition() As Long
  • Clone() As Collection
  • GetField(RecordNum As Long, FieldName As String) As Variant
  • GetRecord(RecordNum As Long) As clsRecord
  • RecordCount() As Long

Die Methoden der Klasse:

  • ClearRecordset()
  • DelRecord(RecordNum As Long) As Boolean
  • DumpFields(FieldList() As String)
  • DumpRecordset()
  • Find(FieldName As String, sMatch As Variant, _
      Optional FindNext As Boolean = False, _
      Optional ExactMatch As Boolean = False) As clsRecord
  • GotoRecord(RecordNum As Long) As clsRecord
  • LockUpdate(Optional vLock As Boolean = True)
  • NewRecord(oRecord As clsRecord)
  • MoveRecord(ByVal MoveType As RSMoveType) As clsRecord
  • SaveRecord(RecordNum As Long, oRecord As clsRecord) As Boolean
  • Sort(FieldName As String, _
      Optional sOrderASC As Boolean = True) As Boolean

Die Ereignisse der Klasse:

  • RecordMoved(RecordNum As Long, oRecord As clsRecord)
  • DumpField(vData As Variant)
  • Dump(oRecord As clsRecord)

Funktionen des virtuellen Recordsets

Nachfolgend möchten wir Ihnen nun die einzelnen Funktionen unseres virtuellen Recordsets vorstellen.

Der allgemeine Deklarationsteil der Klasse

Option Explicit
 
Public Enum RSMoveType
  RSFirst = 0
  RSPrev = 1
  RSNext = 2
  RSLast = 3
End Enum
 
' Ereignisse der Klasse
Public Event RecordMoved(RecordNum As Long, oRecord As clsRecord)
Public Event DumpField(vData As Variant)
Public Event Dump(oRecord As clsRecord)
 
' Private Klassen-Vars
Private colRS As Collection
Private cLockUpdate As Boolean
Private RecPTR As Long

Die öffentliche Aufzählung (Enum) "RSMoveType" definiert die Bewegungsrichtung des Datensatzzeigers innerhalb der Collection. Der Datensatzzeiger selber ist die Private Long-Variable "RecPTR". Dieser Zeiger markiert die Position innerhalb der Collection auf den aktuellen Record.

Die Initialisierung und Terminierung der Klasse

' Klasse initialisieren
' =====================
Private Sub Class_Initialize()
  Set colRS = New Collection
  cLockUpdate = False
  RecPTR = 0
End Sub
 
' Klasse terminieren
' ==================
Private Sub Class_Terminate()
  Set colRS = Nothing
End Sub

Die Eigenschaft "AbsolutePosition"
Sie erhalten die aktuelle Position des Datensatzzeigers auf ein Record innerhalb der Collection. Die Eigenschaft ist Readonly.

' Eigenschaft "AbsolutePosition" (Read)
' =====================================
Public Property Get AbsolutePosition() As Long
  AbsolutePosition = RecPTR
End Property

Die Eigenschaft "Clone"
Clone liefert Ihnen einen Verweis auf die Collection der Klasse. Sie haben hiermit die Möglichkeit, direkt auf die einzelnen Elemente eines Records zugreifen zu können.

' Eigenschaft "Clone"
' ===================
Public Property Get Clone() As Collection
  Set Clone = colRS
End Property

Die Eigenschaft "GetField"
Mit GetField können Sie gezielt einen Wert aus einem Feld auslesen. GetField arbeitet hier wie eine 2D Matrix. Abgefragt wird die Recordnummer (RecordNum) und der Feldname (FieldName). Hinweis: Der Datensatzzeiger wird dabei nicht verschoben. Der Rückgabewert der Eigenschaft ist vom Typ Variant.

' Eigenschaft "GetField" (Read)
' =============================
Public Property Get GetField(RecordNum As Long, FieldName As String) As Variant
  Dim dRecord As clsRecord
 
  ' Rückgabewert ist der Feldinhalt aus der Recordnum
  ' und dem Feldnamen (Matrix). Dabei wird der
  ' Datensatzzeiger nicht verändert.
  If (RecordNum > 0) And (RecordNum <= colRS.Count) Then
    Set dRecord = colRS.Item(RecordNum)
    GetField = CallByName(dRecord, FieldName, VbGet)
    Set dRecord = Nothing
  End If
End Property

Die Eigenschaft "GetRecord"
Analog der Eigenschaft GetField liefert GetRecord das komplette Item, also einen Record aus der Collection. Als Parameter wird wieder die Recordnummer (RecordNum) erwartet. Der Rückgebewert jedoch hier ist vom Type "clsRecord". Hinweis: Der Datensatzzeiger wird dabei nicht verschoben.

' Eigenschaft "GetRecord" (Read)
' ==============================
Public Property Get GetRecord(RecordNum As Long) As clsRecord
  ' Rückgabewert ist der Record(RecordNum). Der
  ' Datensatzzeiger wird nicht verändert.
  If (RecordNum > 0) And (RecordNum <= colRS.Count) Then
    Set GetRecord = colRS.Item(RecordNum)
  End If
End Property

Die Eigenschaft "RecordCount"
Diese Eigenschaft bedarf keiner großen Erklärung - sie liefert die Anzahl Records in der Collection.

' Eigenschaft "RecordCount" (Read)
' =============================
Public Property Get RecordCount() As Long
  RecordCount = colRS.Count
End Property

Methoden der Klasse, Teil 1

Die Methode "ClearRecordset"
Rufen Sie diese Methode auf, so werden alle Records aus dem Recordset gelöscht. Benutzen Sie ClearRecordset immer dann, wenn Sie das Recordset neu aufbauen, z.B. beim Laden der Records aus einer externen Datei.

' Methode "ClearRecordset"
' ========================
Public Sub ClearRecordset()
  ' löscht die gesamte Collection
  Set colRS = New Collection
End Sub

Die Methode "DelRecord"
Es wird ein Record aus dem Recordset gelöscht. Als Parameter übergeben Sie die Recordnummer (RecordNum). Die Methode liefert "True", wenn das Löschen erfolgreich war - andernfalls "False". Wurde ein Record gelöscht, so wird das Ereignis "RecordMoved" ausgelöst.

' Methode "DelRecord"
' ===================
Public Function DelRecord(RecordNum As Long) As Boolean  
  ' löscht den Datensatz(RecordNum) aus der Collection
  If (RecordNum < 0) Or (RecordNum > colRS.Count) Then
     DelRecord = False
     Exit Function
  End If
 
  colRS.Remove RecordNum
 
  ' ggf. Datensatzpointer anpassen
  If RecPTR > colRS.Count Then RecPTR = colRS.Count
 
  ' Ereignis "RecordMoved" auslösen
  If Not cLockUpdate Then RaiseEvent RecordMoved(RecPTR, colRS.Item(RecPTR))
 
  ' Rückgabewert setzen
  DelRecord = True
End Function

Hinweis: Verwenden Sie "DelRecord" in Kombination mit der Eigenschaft "AbsolutePosition" um den aktuellen Record aus dem Recordset zu löschen.

Beispiel:

With myRecordSet
  Call .DelRecord(.AbsolutePosition)
End With

Die Methode "DumpFields"
DumpFields gibt alle angegebenen Felder über das Ereignis "DumpField" an die aufrufende Form zurück. Dabei wird die gesamte Collection durchlaufen. Übergeben Sie ein String-Array mit den Feldnamen, die gedumpt werden sollen. Diese Methode wurde speziell dafür entwickelt, um die Daten in eine externe Datei zu speichern.

' Methode "DumpFields"
' ====================
Public Sub DumpFields(FieldList() As String)
  Dim dRecord As New clsRecord
  Dim vTmp As Variant
  Dim x As Long
 
  ' Es werden die Felder aus der Arg-Liste gedumpt.
  ' Die Schleife läuft dabei über alle Records aus
  ' der Collection. Diese Funktion eignet sich zum
  ' Speichern der Collection in eine Datei.
  For Each dRecord In colRS
    For x = LBound(FieldList) To UBound(FieldList)
      vTmp = CallByName(dRecord, FieldList(x), VbGet)
      ' Ereignis auslösen
      RaiseEvent DumpField(vTmp)
    Next x
  Next dRecord
 
  Set dRecord = Nothing
End Sub

Die Methode "DumpRecordset"
DumpRecordset arbeitet analog der Methode DumpFields, jedoch werden hier über das Ereignis "Dump" alle Records an die aufrufende Form zurückgegeben. Diese Methode eignet sich besonders zum Füllen von ungebundenen List-Controls wie das ListView oder aber auch das sevDatagrid2 im List-Modus.

' Methode "Dumprecordset"
Public Sub DumpRecordset()
  Dim dRecord As New clsRecord
 
  ' Es werden alle Records gedumpt.
  ' Die Schleife läuft dabei über die gesamte
  ' Collection. Diese Funktion eignet sich zur
  ' Ausgabe der Records in ein Listenelement
  ' wie z.B. das ListView.
  For Each dRecord In colRS
    RaiseEvent Dump(dRecord)
  Next dRecord
 
  Set dRecord = Nothing
End Sub

Die Methode "Find"
Diese elementare Funktion darf natürlich auch nicht in unserem virtuellen Recordset fehlen ... Übergeben Sie mindestens den Feldnamen sowie einen Suchbegriff nach dem gesucht werden soll.

Die Suchmethode beginnt ab der aktuellen Position innerhalb unseres Recordsets. Wird eine Übereinstimmung mit dem Suchbegriff gefunden, wird der Datensatzzeiger auf diesen Record positioniert und das Ereignis "RecordMoved" ausgelöst. Als Rückgabewert der Funktion wird der gefundene Record übergeben.

Wird keine Übereinstimmung gefunden, bleibt der Datensatzzeiger an der ursprünglichen Position. Als Rückgabewert der Funktion wird "Nothing" übergeben.

Über den optionalen 3. Parameter können Sie den Suchmodus bestimmen. Setzen Sie "FindNext" auf True, so beginnt die Suche ab der aktuellen Position + 1 im Recordset.

Mit dem 4. optionalen Parameter legen Sie fest, ob nach einer genauen Übereinstimmung oder nach einem Teilbegriff gesucht werden soll.

' Methode "Find"
' ==============
Public Function Find(FieldName As String, sMatch As Variant, _
  Optional FindNext As Boolean = False, _
  Optional ExactMatch As Boolean = False) As clsRecord
 
  Dim dRecord As New clsRecord
  Dim bFound As Boolean
  Dim vTmp As Variant
  Dim lRet As Long
  Dim tPtr As Long
  Dim x As Long
 
  ' Pointer in Hilfsvariable retten
  tPtr = RecPTR
  If (FindNext = True) And (RecPTR < colRS.Count) Then
    RecPTR = RecPTR + 1
    tPtr = RecPTR
  End If
 
  ' Schleife über alle Records ab der
  ' aktuellen Position
  For x = tPtr To colRS.Count
    Set dRecord = colRS.Item(x)
    vTmp = CallByName(dRecord, FieldName, VbGet)
    bFound = False
 
    ' exakte Suche
    If ExactMatch Then
      If sMatch = vTmp Then
        bFound = True
        Exit For
      End If
 
    ' Suche nach Teilbegriff
    Else
      vTmp = Trim(CStr(UCase(vTmp)))
      sMatch = Trim(CStr(UCase(sMatch)))
      lRet = InStr(1, vTmp, sMatch)
      If lRet > 0 Then
        bFound = True
        Exit For
      End If
    End If
 
    RecPTR = RecPTR + 1
  Next x
 
  ' Suchbegriff gefunden
  If bFound Then
    If Not cLockUpdate Then RaiseEvent RecordMoved(RecPTR, colRS.Item(RecPTR))      
    Set Find = colRS.Item(RecPTR)
  Else
    RecPTR = tPtr
    Set Find = Nothing
  End If
 
  If Not dRecord Is Nothing Then Set dRecord = Nothing
End Function

Die Methode "GotoRecord"
Mit GotoRecord positionieren Sie den Datensatzzeiger innerhalb des Recordsets auf einen Record. Dabei wird das Ereignis "RecordMoved" ausgelöst. Als Parameter erwartet die Methode eine Recordnummer (RecordNum). Der Rückgabewert der Methode ist das Record, auf dem der Datensatzzeiger positioniert wurde.

' Methode "GotoRecord"
' ====================
Public Function GotoRecord(RecordNum As Long) As clsRecord
  If (RecordNum < 0) Or (RecordNum > colRS.Count) Then
    GotoRecord = Nothing
    Exit Function
  End If
 
  RecPTR = RecordNum
 
  Set GotoRecord = colRS.Item(RecPTR)
  If Not cLockUpdate Then RaiseEvent RecordMoved(RecPTR, colRS.Item(RecPTR))
End Function

Die Methode "LockUpdate"
Wird LockUpdate auf True gesetzt, so wird der Aufruf des Ereignisses "RecordMoved" aus allen Methoden unterdrückt. Dieses kann hilfreich sein, wenn Sie z.B. das Recordset aus einer externen Datei laden (siehe auch die Methode "NewRecord").

' Methode "LockUpdate"
' ===================
Public Sub LockUpdate(Optional vLock As Boolean = True)
  cLockUpdate = vLock
End Sub

Methoden der Klasse, Teil 2

Die Methode "NewRecord"
Mit NewRecord wird dem Recordset ein neuer Record hinzugefügt. Dabei wird der Record immer hinten an das Recordset angehängt. Der Datensatzzeiger wird dabei automatisch auf den angefügten Record gesetzt und das Ereignis "RecordMoved" ausgelöst (siehe auch "LockUpdate"). Übergeben wird der Methode natürlich ein gefüllter Record.

' Methode "NewRecord"
' ===================
Public Sub NewRecord(oRecord As clsRecord)
  If oRecord Is Nothing Then Exit Sub
 
  colRS.Add oRecord
  RecPTR = colRS.Count
  If Not cLockUpdate Then RaiseEvent RecordMoved(RecPTR, colRS.Item(RecPTR))
End Sub

Die Methode "MoveRecord"
Diese Methode positioniert ebenfalls den Datensatzzeiger. Die Bewegungsrichtung wird dabei über die Aufzählung (Enum) "RSMoveType" bestimmt. Es stehen folgende Aufzählungstypen zur Verfügung:

  • RSFirst     (erster Record)
  • RSPrev     (vorheriger Record)
  • RSNext     (nächster Record)
  • RSLast     (letzter Record)

Auch hier wird das Ereignis "RecordMoved" ausgelöst. Als Rückgabewert der Methode wird das Record, auf dem sich der Datensatzzeiger befindet, zurückgegeben.

' Methode "MoveRecord"
' ====================
Public Function MoveRecord(ByVal MoveType As RSMoveType) As clsRecord
  If RecPTR <= 0 Then Exit Function
 
  ' Diese Methode positioniert den Datensatzzeiger.
  ' Die Richtung wird über die Aufzählung (Enum)
  ' "RSMoveType" bestimmt.
  Select Case MoveType
    Case RSFirst: RecPTR = 1
 
    Case RSPrev
      If RecPTR > 1 Then RecPTR = RecPTR - 1
 
    Case RSNext
      If RecPTR < colRS.Count Then RecPTR = RecPTR + 1
 
    Case RSLast: RecPTR = colRS.Count
  End Select
 
  Set MoveRecord = colRS.Item(RecPTR)
  If Not cLockUpdate Then RaiseEvent RecordMoved(RecPTR, colRS.Item(RecPTR))
End Function

Die Methode "SaveRecord"
Mit SaveRecord haben Sie die Möglichkeit, einen bestehenden Record zu speichern. Editieren Sie einen Record in Ihrer Form und benutzen Sie diese Methode in Kombination mit der Eigenschaft "AbsolutePosition", um die Änderungen an die ursprüngliche Position im Recordset zurückzuschreiben. 

' Methode "SaveRecord"
' ====================
Public Function SaveRecord(RecordNum As Long, oRecord As clsRecord) As Boolean  
  If (RecordNum < 0) Or (RecordNum > colRS.Count) Then
    SaveRecord = False
    Exit Function
  End If
 
  ' Es werden die Änderungen eines Records
  ' in die Collection zurückgeschrieben.
  colRS.Remove RecordNum
  If RecordNum <= colRS.Count Then
    colRS.Add oRecord, , RecordNum
  Else
    colRS.Add oRecord
  End If
  SaveRecord = True
End Function

Die Methode "Sort"
Und zu guter Letzt darf natürlich auch eine Sortierroutine nicht fehlen! Eigens für diese Klasse wurde eine spezielle Routine zum Sortieren einer Collection entwickelt, da Sie eine Collection nicht mit den herkömmlichen Sortierverfahren sortieren können. Die Routine arbeitet über eine Swap-Collection, die direkt in sortierter Form neu aufgebaut wird. Nach der Sortierung wird die Swap-Collection wieder der eigentlichen Collection zugewiesen.

Als Sortierkriterium geben Sie den Feldnamen an, nach dem sortiert werden soll. Als optionaler 2. Parameter legen Sie die Sortierrichtung auf- oder absteigend fest. Der Default ist aufsteigend.

' Methode "Sort"
' ==============
Public Function Sort(FieldName As String, _
  Optional sOrderASC As Boolean = True) As Boolean
 
  Dim dRecord As New clsRecord
  Dim cRecord As New clsRecord
  Dim tmpCol As Collection
  Dim vOrg As Variant
  Dim vComp As Variant
  Dim bFound As Boolean
  Dim lPos As Long
 
  On Error GoTo Sort_Error
  If colRS.Count <= 1 Then Exit Function
  Set tmpCol = New Collection
 
  ' Äußere Schleife über alle Records in colRS
  For Each dRecord In colRS
    ' Value aus dem Feldnamen holen
    vOrg = CallByName(dRecord, FieldName, VbGet)
 
    ' Variablen initialisieren
    bFound = False
    lPos = 0
 
    ' Innere Schleife über alle Records aus
    ' der Swap-Collection
    For Each cRecord In tmpCol
      lPos = lPos + 1
 
      ' Value aus dem Feldnamen holen
      vComp = CallByName(cRecord, FieldName, VbGet)
 
      ' Feldinhalte vergleichen ASC
      If sOrderASC Then
        If vOrg < vComp Then
          bFound = True
          ' Record einfügen wenn kleiner
          tmpCol.Add dRecord, , lPos
          Exit For
        End If
 
      ' Feldinhalte vergleichen DESC
      Else
        If vOrg > vComp Then
          bFound = True
          ' Record einfügen wenn größer
          tmpCol.Add dRecord, , lPos
          Exit For
        End If
      End If
    Next cRecord
 
    ' ggf. Record anhängen
    If Not bFound Then tmpCol.Add dRecord
  Next dRecord
 
  ' Swap-Collection zuweisen
  Set colRS = tmpCol
  RecPTR = 1
  If Not cLockUpdate Then RaiseEvent RecordMoved(RecPTR, colRS.Item(RecPTR))
 
  Set tmpCol = Nothing
  Set dRecord = Nothing
 
  ' Rückgabewert setzen
  Sort = True
  Exit Function
 
Sort_Error:
  Sort = False
  If Not tmpCol Is Nothing Then Set tmpCol = Nothing
  If Not dRecord Is Nothing Then Set dRecord = Nothing
End Function

So, das war´s. In unseren Beispielprojekten zeigen wir Ihnen, wie Sie die Klasse einmal mit einem ListView und zum anderen mit dem  sevDataGrid v2.0 nutzen können.

Schlussbemerkung:

Die Klasse ist so aufgebaut, dass Sie sie nicht nur als Frontend, sondern auch ohne jegliche Oberfläche, also als Backend nutzen können.

Wir wünschen Ihnen gutes Gelingen mit dem virtuellen Recordset.
Roland Wutzke

Dieser Workshop wurde bereits 31.981 mal aufgerufen.

Über diesen Workshop im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2017 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel