Rubrik: Datenbanken | VB-Versionen: VB5, VB6 | 01.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 Wutzke | Bewertung: | Views: 37.525 |
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