ich habe mal eine blöde Frage zu dem Makro der Beispieldatei «Beispiel_Combobox_Listfeld.odb».
Wenn ich statt des Formulars das Unterformular ansprechen möchte, weil dort die Kombinationsfelder sind, wie muss ich dieses ändern?
Code: Alles auswählen
REM ***** BASIC *****
REM Das Modul "Comboboxen" macht aus den Formularfeldern zur Eingabe und Auswahl von Werten (Kombinationsfelder)
REM Listenfelder mit Eingabemöglichkeiten. Dazu werden neben den Kombinationsfeldern im Formular die jeweils an
REM die zugrundeliegende Tabelle zu übergebenden Schlüsselfeldwerte in separaten numerischen Feldern abgelegt.
REM Die Schlüssel aus diesen Feldern werden beim Start des Formulars ausgelesen und das Kombinationsfeld auf den entsprechenden
REM Inhalt eingestellt. Dies ist allerdings nur dann notwendig, wenn nicht über eine Abfrage auch der Inhalt der Kombinationsfelder
REM dargestellt wird.
REM Wird der Inhalt des Kombinationsfeldes geändert, so wird er neu abgespeichert und der neue Primärschlüssel zum Abspeichern
REM in der Haupttabelle in das entsprechende numerische Feld übertragen.
REM Damit das Makro sicher eine Datensatzaktion auch in den Kombinationsfeldern anzeigt sollte über "Datensatzaktion_erzeugen"
REM bei Änderung des Inhaltes des Kombinationsfeldes dem Formular eine Datnsatzänderung mitgeteilt werden. Auch dies ist
REM nur dann notwendig, wenn nicht über eine Abfrage auch der Inhalt der Kombinationsfelder dargestellt wird.
FUNCTION String_to_SQL(st AS STRING)
IF InStr(st,"'") THEN
st = Join(Split(st,"'"),"''")
END IF
String_to_SQL = st
END FUNCTION
SUB TextAnzeigen(oEvent AS OBJECT)
REM Dieses Makro sollte an das Ereignis des Formulars 'Nach dem Datensatzwechsel' gebunden werden
REM Das Makro ist dann notwendig, wenn das Formular nicht über eine entsprechende Abfrage
REM bereits mit den Inhalten der Kombinationsfelder versorgt wird.
DIM oForm AS OBJECT
DIM inCom AS INTEGER
DIM oFeldList AS OBJECT
DIM stFeldID AS STRING
DIM stAbfrage AS STRING
DIM stFeldWert AS STRING
oForm = oEvent.Source
aComboboxen() = Split(oForm.getByName("Comboboxen").Tag,",") 'Ermittelt aus dem Startformular die betroffenen Comboboxen
FOR inCom = LBound(aComboboxen) TO UBound(aComboboxen)
oFeldList = oForm.getByName(Trim(aComboboxen(inCom)))
stFeldID = oForm.getString(oForm.findColumn(oFeldList.Tag)) 'Wert des Fremdschlüsselfeldes, als String abgefragt, da sonst als Default '0'
oFeldList.Refresh() REM Refresh(), damit neu eingegebene Werte angezeigt werden
REM Wert des ID-Feldes auslesen
IF stFeldID <> "" THEN
REM Nur wenn das ID-Feld nicht leer ist: oVerbindung mit der oDatenquelle herstellen
stAbfrage = oFeldList.ListSource 'Der Inhalt der Abfrage des Kombinationsfeldes wird ausgelesen.
IF InStr(stAbfrage,"order by") > 0 THEN
stSql = Left(stAbfrage, InStr(stAbfrage,"order by")-1)
ELSE
stSql = stAbfrage
END IF
IF InStr(stSql,"where") THEN
st = Right(stSql, Len(stSql)-InStr(stSql,"where")-4)
IF InStr(Left(st, InStr(st,"=")),".""ID""") THEN
a() = Split(Right(st, Len(st)-InStr(st,"=")-1),".")
ELSE
a() = Split(Left(st, InStr(st,"=")-1),".")
END IF
stSql = stSql + "AND "+a(0)+".""ID"" = "+stFeldID
ELSE
stSql = stSql + "WHERE ""ID"" = "+stFeldID
END IF
oDatenquelle = ThisComponent.Parent.CurrentController
IF NOT (oDatenquelle.isConnected()) THEN
oDatenquelle.connect()
END IF
oVerbindung = oDatenquelle.ActiveConnection()
oSQL_Anweisung = oVerbindung.createStatement()
REM SQL-Anweisung nach den in dem Kombinationsfeld dargestellten Feldern formulieren
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
stFeldWert = oAbfrageergebnis.getString(1)' Erstes Datenfeld
WEND ' nächster Datensatz
REM Kombinationsfeld auf den aus der Abfrage sich ergebenden Textwert einstellen
oFeldList.Text = stFeldWert
ELSE
oFeldList.Text = ""
END IF
NEXT inCom
END SUB
SUB TextAuswahlWertSpeichern(oEvent AS OBJECT)
REM Dieses Makro sollte an die folgenden Ereignisse des Formulars gebunden werden: 'Vor der Datensatzaktion'
DIM NameIDFeld AS STRING
DIM NameTabellenFeld1 AS STRING
DIM NameTabellenFeld2 AS STRING
DIM Feldtrenner AS STRING
DIM NameTabelle1 AS STRING
DIM NameTabelle2 AS STRING
DIM NameTab12ID AS STRING
DIM Position AS INTEGER
DIM oFeldList AS OBJECT
DIM oFeld AS OBJECT
DIM oForm AS OBJECT
DIM stAbfrage AS STRING
DIM stInhalt AS STRING
DIM i AS INTEGER
DIM stInhaltFeld2 AS STRING
DIM a_stTeile() AS STRING
DIM stmsgbox1 AS STRING
DIM stmsgbox2 AS STRING
DIM inID1 AS INTEGER
DIM inID2 AS INTEGER
DIM LaengeFeld1 AS INTEGER
DIM LaengeFeld2 AS INTEGER
IF InStr(oEvent.Source.ImplementationName,"ODatabaseForm") THEN
REM 'Vor der Datensatzaktion' Löst zwei verschiedene Implementationen aus.
REM Als Ereignis reicht eine Implementation. Hierüber wird das Formular ermittelt.
oForm = oEvent.Source
aComboboxen() = Split(oForm.getByName("Comboboxen").Tag,",") 'Ermittelt aus dem Startformular die betroffenen Comboboxen
FOR inCom = LBound(aComboboxen) TO UBound(aComboboxen)
NameTabelle2 = "" 'Falls mehrere Combofelder in einem Formular liegen muss die zweite Tabelle von einem vorhergehenden Feld auf Leer gesetzt werden
a() = Split(Trim(aComboboxen(inCom)),">") 'Aufsplittung, damit auch Tabellenkontrollfelder bearbeitet werden können
IF Ubound(a) > 0 THEN
oFeldList = oForm.getByName(a(0)).getByName(a(1)) 'Feld im Tabellenkontrollfeld
ELSE
oFeldList = oForm.getByname(a(0))
END IF
stAbfrage = oFeldList.ListSource 'Der Inhalt der Abfrage des Kombinationsfeldes wird ausgelesen.
aFelder() = Split(stAbfrage, """")
stInhalt = ""
FOR i=LBound(aFelder)+1 TO UBound(aFelder)
REM Der Inhalt der Abfrage wird von Ballast befreit.
REM Die Teile werden anschließend über eine nicht übliche Zeichenkombination zu einem Array wieder zusammengefügt.
REM FROM trennt die sichtbare Feldanzeige von der Tabellenbezeichnung.
REM WHERE trennt die Beziehungsdefinition von der Tabellenbezeichnung. Joins werden nicht unterstützt.
IF Trim(UCASE(aFelder(i))) = "ORDER BY" THEN
EXIT FOR
ELSEIF Trim(UCASE(aFelder(i))) = "FROM" THEN
stInhalt = stInhalt+" §§ "
ELSEIF Trim(UCASE(aFelder(i))) = "WHERE" THEN
stInhalt = stInhalt+" §§ "
ELSE
stInhalt = stInhalt+Trim(aFelder(i))
END IF
NEXT i
aInhalt() = Split(stInhalt, " §§ ")
REM Die sichtbare Feldanzeige wird gegebenenfalls in Inhalte aus verschiedenen Feldern aufgeteilt.
aErster() = Split(aInhalt(0),"||")
IF UBound(aErster) > 0 THEN
IF UBound(aInhalt) > 1 THEN
REM Der erste Teil enthält mindestens 2 Felder. Die Felder haben zu Beginn eine Tabellenbezeichnung.
REM Der zweite Teil enthält zwei Tabellenbezeichnungen, die aber schon aus dem ersten Teil ermittelt werden können.
REM Der dritte Teil enthält eine Beziehung über einen Fremdschlüssel mit "=" getrennt
aTest() = Split(aErster(0),".")
NameTabelle1 = aTest(0)
NameTabellenFeld1 = aTest(1)
Erase aTest
Feldtrenner = Join(Split(aErster(1),"'"),"")
aTest() = Split(aErster(2),".")
NameTabelle2 = aTest(0)
NameTabellenFeld2 = aTest(1)
Erase aTest
aTest() = Split(aInhalt(2),"=")
aTest1() = Split(aTest(0),".")
IF aTest1(1) <> "ID" THEN
NameTab12ID = aTest1(1)
IF aTest1(0) = NameTabelle1 THEN
Position = 2
ELSE
Position = 1
END IF
ELSE
Erase aTest1
aTest1() = Split(aTest(1),".")
NameTab12ID = aTest1(1)
IF aTest1(0) = NameTabelle1 THEN
Position = 2
ELSE
Position = 1
END IF
END IF
ELSE
REM Der erste Teil enthält zwei Feldbezeichnungen ohne Tabellenbezeichnung, eventuell mit Trenner
REM Der zweite Teil enthält die Tabellenbezeichnung.
REM Ein dritter Teil ist nicht vorhanden.
IF UBound(aErster) > 1 THEN
NameTabellenFeld1 = aErster(0)
Feldtrenner = Join(Split(aErster(1),"'"),"")
NameTabellenFeld2 = aErster(2)
ELSE
NameTabellenFeld1 = aErster(0)
NameTabellenFeld2 = aErster(1)
END IF
NameTabelle1 = aInhalt(1)
END IF
ELSE
REM Es existiert nur ein Feld, das aus einer Tabelle stammt
NameTabellenFeld1 = aErster(0)
NameTabelle1 = aInhalt(1)
END IF
LaengeFeld1 = Spaltengroesse(NameTabelle1,NameTabellenFeld1)
IF NameTabellenFeld2 <> "" THEN
IF NameTabelle2 <> "" THEN
LaengeFeld2 = Spaltengroesse(NameTabelle2,NameTabellenFeld2)
ELSE
LaengeFeld2 = Spaltengroesse(NameTabelle1,NameTabellenFeld2)
END IF
ELSE
LaengeFeld2 = 0
END IF
stInhalt = oFeldList.getCurrentValue()
REM Leertasten am Anfang und Ende der Eingabe gegebenenfalls entfernen
stInhalt = Trim(stInhalt)
IF stInhalt <> "" THEN
IF NameTabellenFeld2 <> "" THEN
REM Wenn ein zweites Tabellenfeld existiert muss der Inhalt des Kombinationsfeldes gesplittet werden.
a_stTeile = Split(stInhalt, Feldtrenner, 2)
REM Es werden maximal 2 Teile erzeugt
IF Position = 2 THEN
stInhalt = Trim(a_stTeile(0))
IF UBound(a_stTeile()) > 0 THEN
stInhaltFeld2 = Trim(a_stTeile(1))
ELSE
stInhaltFeld2 = ""
END IF
ELSE
stInhaltFeld2 = Trim(a_stTeile(0))
IF UBound(a_stTeile()) > 0 THEN
stInhalt = Trim(a_stTeile(1))
ELSE
stInhalt = ""
END IF
END IF
END IF
IF (LaengeFeld1 > 0 AND Len(stInhalt) > LaengeFeld1) OR (LaengeFeld2 > 0 AND Len(stInhaltFeld2) > LaengeFeld2) THEN
stmsgbox1 = "Das Feld "+NameTabellenFeld1+" darf höchstens "+LaengeFeld1+ " Zeichen lang sein."+CHR(13)
stmsgbox2 = "Das Feld "+NameTabellenFeld2+" darf höchstens "+LaengeFeld2+ " Zeichen lang sein."+CHR(13)
IF (LaengeFeld1 > 0 AND Len(stInhalt) > LaengeFeld1) AND (LaengeFeld2 > 0 AND Len(stInhaltFeld2) > LaengeFeld2) THEN
msgbox ("Der eingegebene Text ist zu lang."+CHR(13)+stmsgbox1+stmsgbox2+"Bitte den Text kürzen.",64,"Fehlerhafte Eingabe")
ELSEIF (LaengeFeld1 > 0 AND Len(stInhalt) > LaengeFeld1) THEN
msgbox ("Der eingegebene Text ist zu lang."+CHR(13)+stmsgbox1+"Bitte den Text kürzen.",64,"Fehlerhafte Eingabe")
ELSE
msgbox ("Der eingegebene Text ist zu lang."+CHR(13)+stmsgbox2+"Bitte den Text kürzen.",64,"Fehlerhafte Eingabe")
END IF
ELSE
REM Der Inhalt des Feldes muss für das Zeichen ' maskiert werden. Außerdem wird er gleich in ' ' eingeschlossen.
stInhalt = String_to_SQL(stInhalt)
IF stInhaltFeld2 <> "" THEN
stInhaltFeld2 = String_to_SQL(stInhaltFeld2)
END IF
REM Der vorherige Wert des Feldes oFeld (Fremdschlüsselwert) wird vollkommen ignoriert. Dies ist notwendig, damit z.B. eine Änderung des Eintrags nicht
REM andere Datensätze mit gleichem Fremdschlüssel ebenfalls ändert.
REM Beispiel: 48431 Rheine habe die ID 14. Der Wert wird geänder auf 48429 Rheine. Würde jetzt bei der ID 14 tatsächlich 48429 in das Postleitzahlenfeld
REM eingetragen, so würden alle Datensätze von 48431 auf 48429 geändert. Ehemals richtige Adressen würden anschließend falsch geschrieben, da Rheine mehrere Postleitzahlen hat.
inID1 = -1 'Zu ermittelnde ID auf einen Wert setzen, den keine ID hat, damit sicher ist, dass nicht als Standard für Integerfelder 0 wiedergegeben wird
inID2 = -1 'Standard zu Beginn festlegen. inID2 ist aus dem Formular her auf jeden Fall unbekannt
REM Datenbankverbindung erzeugen
oDatenquelle = ThisComponent.Parent.CurrentController
If NOT (oDatenquelle.isConnected()) Then
oDatenquelle.connect()
End If
oVerbindung = oDatenquelle.ActiveConnection()
oSQL_Anweisung = oVerbindung.createStatement()
IF NameTabellenFeld2 <> "" AND NOT IsEmpty(stInhaltFeld2) AND NameTabelle2 <> "" THEN
REM Wenn ein zweites Tabellenfeld existiert muss zuerst die zweite Abhängigkeit geklärt werden. Zuerst wird geklärt, ob der Eintrag eventuell schon vorhanden ist.
stSql = "SELECT ""ID"" FROM """+NameTabelle2+""" WHERE """+NameTabellenFeld2+"""='"+stInhaltFeld2+"'"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID2 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
REM Entweder existiert jetzt ein Wert ab 0 für inID2 (der Eintrag war dann bereits in Tabelle 2 zu finden) oder inID2 ist weiterhin -1. Dann muss der Eintrag eingefügt werden.
IF inID2 = -1 THEN
stSql = "INSERT INTO """+NameTabelle2+""" ("""+NameTabellenFeld2+""") VALUES ('"+stInhaltFeld2+"') "
oSQL_Anweisung.executeUpdate(stSql)
REM Und die entsprechende ID direkt wieder auslesen
stSql = "CALL IDENTITY()"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID2 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
END IF
REM Es wird getestet, ob die Kombination aus inID2 und stInhalt in der ersten Tabelle bereits existiert.
REM Dies kann bei Neuauswahl eines Wertes durch das Combofeld sein. Dann existiert zwar noch kein Eintrag in dem Formularfeld für die ID (oFeld).
REM Ohne die Abfrage würden aber die Werte aus der Combobox beständig bei Neueinträgen verdoppelt.
stSql = "SELECT ""ID"" FROM """+NameTabelle1+""" WHERE """+NameTab12ID+"""='"+inID2+"' AND """+NameTabellenFeld1+""" = '"+stInhalt+"'"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID1 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
REM Existiert kein Eintrag für den Primärschlüssel, auch nach vorhergehendem Test nicht, so ist die Kombination neu zu erstellen und der Schlüssel auszulesen.
REM Nach einem ursprünglich vorhandenen Schlüssel in oFeld wird nicht gefragt. Änderungen eines Datensatzes sind nicht vorgesehen, da sonst andere Datensätze
REM mit geändert würden. Dies könnte z.B. bei der Änderung einer Postleitzahl für einen Ort dazu führen, dass mehrere Postleitzahlen für einen Ort nicht existieren könnten.
IF inID1 = -1 THEN
stSql = "INSERT INTO """+NameTabelle1+""" ("""+NameTabellenFeld1+""","""+NameTab12ID+""") VALUES ('"+stInhalt+"','"+inID2+"') "
oSQL_Anweisung.executeUpdate(stSql)
REM Und die entsprechende ID direkt wieder auslesen
stSql = "CALL IDENTITY()"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID1 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' naechster Datensatz
END IF
END IF
IF NameTabellenFeld2 <> "" AND NameTabelle2 = "" THEN
stSql = "SELECT ""ID"" FROM """+NameTabelle1+""" WHERE """+NameTabellenFeld1+"""='"+stInhalt+"' AND """+NameTabellenFeld2+"""='"+stInhaltFeld2+"'"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID1 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
IF inID1 = -1 THEN
REM ... und eine zweite Tabelle nicht existiert
stSql = "INSERT INTO """+NameTabelle1+""" ("""+NameTabellenFeld1+""","""+NameTabellenFeld2+""") VALUES ('"+stInhalt+"','"+stInhaltFeld2+"') "
oSQL_Anweisung.executeUpdate(stSql)
REM Und die entsprechende ID direkt wieder auslesen
stSql = "CALL IDENTITY()"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID1 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
END IF
END IF
IF NameTabellenFeld2 = "" THEN
stSql = "SELECT ""ID"" FROM """+NameTabelle1+""" WHERE """+NameTabellenFeld1+"""='"+stInhalt+"'"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID1 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
IF inID1 = -1 THEN
REM Wenn ein zweites Tabellenfeld nicht existiert
stSql = "INSERT INTO """+NameTabelle1+""" ("""+NameTabellenFeld1+""") VALUES ('"+stInhalt+"') "
oSQL_Anweisung.executeUpdate(stSql)
REM Und die entsprechende ID direkt wieder auslesen
stSql = "CALL IDENTITY()"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
inID1 = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
END IF
END IF
REM ID-Wert in die Spalte der Haupttabelle eintragen
oForm.updateLong(oForm.findColumn(oFeldList.Tag),inID1)
END IF
ELSE
oForm.updateNULL(oForm.findColumn(oFeldList.Tag),NULL)
END IF
NEXT inCom
END IF
END SUB
SUB Datensatzaktion_erzeugen(oEvent AS OBJECT)
REM Das Makro ist dann notwendig, wenn das Formular nicht über eine entsprechende Abfrage
REM bereits mit den Inhalten der Kombinationsfelder versorgt wird.
DIM oForm AS OBJECT
oForm = oEvent.Source.Model.Parent 'Das Formular wird über das auslösende Ereignis bestimmt
oForm.isModified = TRUE 'Erzeugt bei Änderung eine Datensatzaktion
END SUB
FUNCTION Spaltengroesse(Tabellenname AS STRING, Feldname AS STRING) AS INTEGER
REM Über die GUI lässt sich die Zeichenlänge des Datensatzes nicht festlegen wenn 2 Felder in einem Kombinationsfeld zusammengefasst werden.
REM Deshalb wird hier aus der Systemtabelle INFORMATION_SCHEMA.SYSTEM_COLUMNS die jeweilige Spaltengröße ausgelesen.
REM Datenbankverbindung erzeugen
oDatenquelle = ThisComponent.Parent.CurrentController
If NOT (oDatenquelle.isConnected()) Then
oDatenquelle.connect()
End If
oVerbindung = oDatenquelle.ActiveConnection()
oSQL_Anweisung = oVerbindung.createStatement()
stSql="SELECT ""COLUMN_SIZE"" FROM ""INFORMATION_SCHEMA"".""SYSTEM_COLUMNS"" WHERE ""TABLE_NAME"" = '"+Tabellenname+"' AND ""COLUMN_NAME"" = '"+Feldname+"'"
oAbfrageergebnis = oSQL_Anweisung.executeQuery(stSql)' Ergebnis auswerten
WHILE oAbfrageergebnis.next
i = oAbfrageergebnis.getInt(1)' Erstes Datenfeld
WEND ' nächster Datensatz
Spaltengroesse = i
END FUNCTION