PDA

Vollständige Version anzeigen : Ereignis NotInList automatisch auslösen


flew
20.01.2008, 22:51
Hallo zusammen !

Ich versuche mich gerade an den diversen String Funktionen die es gibt.
Zu diesem Zweck füge ich den Inhalt eines Textdokumentes in ein großes Textfeld ein und zerlege diesen Inhalt, welchen ich dann zerlegt wiederum in Textfelder / Kombinationsfelder einfüge.
Das alles in VBA

Daher nun eine Frage.
Ich fülle auch Kombinationsfelder. Die Kombinationsfelder (Datensatzherkünfte sind Tabellen) haben einen Code im Ereignis Bei Nicht In Liste hinterlegt.
Wenn ich dieses Kombinationsfeld nun via VBA mit Text fülle und zum nächsten Textfeld springe, wird der Bei Nicht In Liste Code des Kombifeldes ignoriert.

Ich habe versucht, dieses Kombifeld explizit zu fokussieren und defokussieren (ctl.SetFocus ... ), allerdings ohne gewünschten Erfolg.
Wenn ich den neuen Text jedoch manuell in das Kombifeld eintrage, und möchte das Kombifeld wieder verlassen, arbeitet Access den Code korrekt ab.

Wie kann ich also das Ereignis Bei Nicht In Liste des Kombifeldes aufrufen, also a la cboText.OnNotInList (funktioniert so auch nicht) ?



Grüße
flew

Josef P.
20.01.2008, 23:01
Das Ereignis selbst kannst du per VBA-Code nicht auslösen. Du kannst aber den Code zur Ereignisbehandlung wie eine "normale" Prozedur starten.

Falls du per VBA prüfen willst, ob der Wert vorhanden ist:
if me!cboText.value = me!cboTest.Column(0) then
' Wert vorhanden wenn Column(0) = gebundene Spalte
else
' Wert ist nicht in Datenherkunft
end if

flew
20.01.2008, 23:08
Okay danke,

dann werde ich es so machen.
Ich wollte eben nur umgehen, den Code doppelt zu hinterlegen, daher habe ich nach eine Lösung mittels Punktoperator gesucht o.ä. ( also cboText.OnNotInList ).

Damit der Code nicht doppelt vorhanden ist, sollte er dann wohl in eine Funktion ... denke ich mal
Dann kann ich die Funktion als Ereignis und in der relevanten Prozedur hinterlegen.

Das wäre doch der Kompromis, oder ?


Danke für den Tip wie man überprüft, ob ein Wert bereits vorhanden, ich hätte nun nochmals in die Tabelle schauen lassen und wäre nicht den direkten Weg über die Spalte des Kombifeldes gegangen, was ja auch wieder doppelt gewesen wäre.


Gruß
flew

Anne Berg
20.01.2008, 23:10
Möglicherweise löst auch ein "Call Kombifeld_AfterUpdate" die gewünschte Ereigniskette aus?

Josef P.
20.01.2008, 23:15
Ich wollte eben nur umgehen, den Code doppelt zu hinterlegen, ...
Da hast du mich vermutlich falsch verstanden. Ich meinte, dass du direkt die Sub für die Ereignisbehandlung aufrufen kannst.

Damit der Code nicht doppelt vorhanden ist, sollte er dann wohl in eine Funktion ... denke ich mal
Dann kann ich die Funktion als Ereignis und in der relevanten Prozedur hinterlegen.
Diese Variante würde ich bevorzugen, da damit der Code um vieles verständlicher wird. (Ich mag es zumindest nicht, wenn eine Ereignis-Behandlungsprozedur von anderer Stelle aufgerufen wird. - So gesehen ist es schlimm, dass ich das dann vorschlage - aber vielleicht hoffte ich, dass dir das auch nicht gefällt - und diese Hoffnung ging auf. ;))

Möglicherweise löst auch ein "Call Kombifeld_AfterUpdate" die gewünschte Ereigniskette aus?
... das ist aber nur die Prozedur zur Ereignisbehandlung und nicht das Ereignis.

flew
20.01.2008, 23:18
Leider nein,

die Funktion sei nicht definiert bekomme ich als Meldung.
Wenn ich das AfterUpdate durch NotInList (Call cboText_NotInList) ersetze, bekomme ich die Meldung, das Argument sei nicht optional.

Edit:
Wie kann ich denn generell ein fremdes Ereignis aufrufen, auch wenn ich mich für eine Funktion entschieden habe :)
Also nur interessehalber, ich wüsste nicht wie es geht, vielleicht hilft es mir ja mal irgendwann

Gruß

Anne Berg
20.01.2008, 23:19
Nun, das Ereignis NotInList kannst du - sinnvollerweise - ja nur aufrufen, wenn tatsächlich ein neuer Wert eingetragen wurde.
Ist das denn so sicher?

@flew: Beschreibe bitte etwas ausführlicher, was du tust/versucht hast und zeige den VBA-Code dazu.

Josef P.
20.01.2008, 23:25
so eine Struktur könnte ich mir vorstellen:
Private Sub cboTest_NotInList(NewData As String, Response As Integer)
If DatenAnfuegen(NewData) = True Then
'....
End If
End Sub

Private Function DatenAnfuegen(ByVal NewData As String) As Boolean
'...
End Function

Private Sub IrgendeineAndereSub()

Me!cboTest.value = 'Abc'

'Prüfen ob Wert in Liste vorhanden ist:
If Me!cboTest.value = Me!cboTest.Column(0) Then
'...
Else
DatenAnfuegen Me!cboTest.value
End If
'Anm.: NULL beachten, falls dies möglich sein kann

End Sub

Wie kann ich denn generell ein fremdes Ereignis aufrufen, auch wenn ich mich für eine Funktion entschieden habe
gar nicht. :D
Du könntest aber die Prozedur aufrufen, die auf das Ereignis reagiert. In diesem Fall wäre das cboTest_NotInList.

Private Sub IrgendeineAndereSub()

Dim intResponse As Integer

cboTest_NotInList "Abc", intResponse
'mach etwas mit der Rückgabe von intResponse (falls notwendig)

End Sub

flew
20.01.2008, 23:33
Private Sub cboVereinComDierkstat_NotInList(NewData As String, Response As Integer)

'aus dem ms-office-forum
'http://www.ms-office-forum.net/forum/showthread.php?t=225198

Dim rsU As DAO.Recordset

Set rsU = DBEngine(0)(0).OpenRecordset("tblVerein", dbOpenDynaset)
rsU.AddNew
rsU![Verein] = NewData
rsU.Update
If Not rsU Is Nothing Then rsU.Close: Set rsU = Nothing
Me![cboVereinComDierkstat].SetFocus
Me![cboVereinComDierkstat] = NewData
Response = acDataErrAdded
Me![cboVereinComDierkstat].Requery
If Not rsU Is Nothing Then rsU.Close: Set rsU = Nothing

End Sub

Private Sub cmdEintragen_Click()

Dim db As DAO.Database
Dim strSQL As String
Dim ctl As Control
Dim txtAllComstat As TextBox

Set db = CurrentDb
Set txtAllComstat = Me!txtMannschaftComDierkstat

'zuerst Trennzeichen durch Zeilenumbruch ersetzen
txtAllComstat = Replace(txtAllComstat, Chr(9), vbCrLf)
'am Ende einen Zeilenumbruch, um Fehlerauswurf zu vermeiden
txtAllComstat = txtAllComstat & vbCrLf

Do While txtAllComstat <> ""
For i = 1 To 5
For Each ctl In Me.Controls
If ctl.Tag = "Comstat" & i Then
ctl = Left(txtAllComstat, InStr(txtAllComstat, vbCrLf) - 1)
' If ctl.Name = "cboVereinComDierkstat" Then
' Call cboVereinComDierkstat_NotInList(NewData As String, Response As Integer)
' End If
'wichtig dass count - Paratemer der Replace Funktion gesetzt wird !
'jedoch wird Schleife dadurch deutlich langsamer
txtAllComstat = Replace(txtAllComstat, ctl.Value & vbCrLf, "", 1, 1)
End If
Next
Next

'folgendes aendern, und nun combiboxes beruecksichtigen
''****************************************************************************** **************************
' 'pruefen auf neuen Verein
' If Me!txtVereinComDierkstat <> "Nicht-Bundesligist" Then
' strSQL = "Select Count(Verein) As AnzFld From tblVerein Where Verein='" & Me!txtVereinComDierkstat & "'"
'
' If db.OpenRecordset(strSQL, dbOpenDynaset)(0) = 0 Then
' 'falls neuer Verein, dann hinzufuegen
' strSQL = "INSERT INTO tblVerein ( Verein ) " & _
' "Values ('" & Me!txtVereinComDierkstat & "')"
' CurrentDb.Execute strSQL, dbFailOnError
' End If
' End If
''****************************************************************************** **************************

Loop

SQLStr = ""
Set db = Nothing

End Sub



Ich füge also erstmal Text in die Zwischenablage, diesen anschliessend in ein ungebundenes Textfeld eines ungebundenen Formulars.
Der Zwischenspeicher besteht aus mehreren Datensätzen, die ich auch so in eine bestehende Tabellenstruktur einfügen wollte.
Das was ich an Code auskomentiert habe, funktioniert, allerdings probierte ich es auch über das NotInList Ereignis des Kombifeldes.

Ein Datensatz besteht aus einem Spielernamen, Verein, Position, Marktwert, Punkte.
Das ganze ist ein OnlineSpiel, bei dem ich mir eine kleine Übersicht über die Marktwertverläufe von Fussballspielern schaffen wollte :) Da diese nicht abrufbar sind.
Ausserdem konnte ich so dem Umgang mit den String Funktionen üben.
Ich füge also nach und nach fünf kleine Textfelder / Kombifelder.
Wenn ich beim fünften Feld angekommen bin, habe ich dann vor den vollständigen Datensatz via VBA-SQL in die Tabellen zu schreiben und mit dem nächsten Datensatz weiterzumachen.
Es gibt eben eine Tabelle tblVerein, und zu Beginn jeder Saison gibt es drei neue :) und eben das wollte ich auch automatisieren.


EDIT:
Nicht dass ich zu faul sei, einmal pro Jahr drei Datensätze manuell einzufügen....
... es gibt ja aber auch immer wieder neue Spieler etc.
Dort wäre dann dasselbe Vorgehen.
Ich werden mir mich mal dem Versuch einer Funktion annehmen

Gruß

Anne Berg
21.01.2008, 08:13
Hallo,

ich frage mich, wozu du das überhaupt mit einem Formular lösen willst. Soll der Anwender zusehen wie sich die Felder quasi wie von Zauberhand füllen? Im Zweifelsfall geht das doch so schnell, dass er das gar nicht mitbekommt... :confused:

flew
21.01.2008, 08:48
Hallo,

nein, es soll keine Zauber-Präsentation werden.
Der 'Anwender' bin ja auch ich.

Sehen tut man da auch nichts, der Bildschirm wird erst aktualisiert, wenn die Prozedur fertig ist.
Einen Datensatz verlege ich aus dem großen Textfeld in die fünf kleineren, damit ich den anschließenden SQL String einfacher zusammensetzen kann.
Sonst müsste ich doch anstatt einen Verweis auf ein Formular Steuerelement direkt die längere String-Funktion in den SQL String, welcher dann den Datensatz in die Tabelle einfügt, anwenden.
Ich mache das in einem Formular, da ich den Inhalt der Zwischenablage ja irgendwo einfügen muss .... dachte ich.
Sonst müsste ich doch direkt auf die Zwischenablage zugreifen ?

Anne Berg
21.01.2008, 08:51
Wie kommt denn der Text in die Zwischenablage und wie holst du ihn wieder heraus?

PS: Man kann Textdateien auch per VBA einlesen und die Daten mit Anfügeabfragen in Tabellen speichern.

flew
21.01.2008, 08:52
Ich kopiere manuell eine Liste aus einem Browserfenster,
diese füge ich dann manuell in das Textfeld in das Formular ein (per Copy & Paste)

Anne Berg
21.01.2008, 08:55
...und wie geht's weiter?

Ich denke, die Vorgeschichte zur "NotInList"-Frage spielt hier die größere Rolle und ich glaube, das Formularereignis ist verzichtbar und der Ablauf könnte wohl komplett in VBA gelöst werden.

flew
21.01.2008, 09:17
Hey,

ich habe nun die DB angehangen.
Wie gesagt, es muss der Inhalt der Textdatei in das große Textfeld des Formulares kopiert werden. Das mache ich manuell, da ich nicht wüsste, wie ich ein Browserfenster ansprechen sollte. In der Textfdatei ist der Text nun nur, damit ein Anhaltspunkt da ist, wie die Daten aussehen.

So, ich wollte mir die Sache halt vereinfachen.
Dazu dachte ich, ändere ich die urspünglichen Textfelder für Spieler, Verein sowie Position in Kombifelder und habe somit erstens direkten Zugriff auf die jeweiligen Primärschlüssel, außerdem könnte ich ja dann direkt auf das Ereignis 'Bei Nicht In Liste' reagieren.
Aber eben da hakte es, da das Ereignis nicht auslöste.

Der SQL Aufruf, welcher dann die Datensätze hinzufügt, habe ich noch nicht vervollständigt, erst wollte ich den Rest hinbekommen.

Es gibt also regelmäßig neue Spieler, die dann in die tblSpieler hinzugefügt werden sollen, sonst soll nur ein neuer Datensatz in die tblSpieler gespeichert werden, wenn er bspw. den Verein wechselte oder eben die Position.

Wie ich das komplett in VBA lösen sollte, wusste ich eben nicht, deswegen bin ich den für mich einfacheren weil anschaulicheren Weg über das Formular gegangen.
(Die beiden Tabellen unten rechts im Beziehungsfenster ignorieren)

Gruß
flew

Anne Berg
21.01.2008, 09:52
Hallo,

ich sehe nicht, wie bzw. wo dieses Kombifeld gefüllt wird.

Hast du vielleicht ein bißchen zu viel gelöscht?
Mir kam es jetzt darauf an, zu sehen wie du die Daten für den Insert aufbereitest... :confused:

flew
21.01.2008, 10:12
Hallo,
nein gelöscht habe ich gar nichts, mehr existiert noch nicht :)

Also die drei Kombifelder haben jeweils als Datensatzherkunft die entsprechenden Tabellen (tblSpieler, tblVerein bzw. tblPos)

Ich füge also den Inhalt der Zwischenablage in das große Textfeld, danach um es zu vereinfachen und um Trennzeichen zu haben, kommt jeder einzelne String in eine eigene Zeile

'zuerst Trennzeichen durch Zeilenumbruch ersetzen
txtAllComstat = Replace(txtAllComstat, Chr(9), vbCrLf)
'am Ende einen Zeilenumbruch, um Fehlerauswurf zu vermeiden
txtAllComstat = txtAllComstat & vbCrLf
MsgBox "erster Schritt!"

(Wenn du das rot markierte bspw. hinzufügst, dann sieht man die Zeilentrennung)

Nun arbeite ich Spalte für Spalte das große Textfeld ab, solange es nicht leer ist.
Die fünf Textfelder/Kombifelder haben jeweils eine Marke, durchnummeriert von 1 bis 5. Ich suche nun also das Steuerelement mit der Marke 1, fülle dies nun mit der obersten Zeile des großen Textfeldes und lösche anschließend diese erste Zeile.
Dann suche ich die Marke 2 .... fülle mit der ersten Zeile .... lösche erste Zeile ...
Das mache ich solange bis Marke 5 gefüllt, mein Datensatz also vollständig ist.

Do While txtAllComstat <> ""
For i = 1 To 5
For Each ctl In Me.Controls
If ctl.Tag = "Comstat" & i Then
ctl = Left(txtAllComstat, InStr(txtAllComstat, vbCrLf) - 1)
' If ctl.Name = "" = Me!cboTest.Column(0) Then
' ' Wert vorhanden wenn Column(0) = gebundene Spalte
' Else
' ' Wert ist nicht in Datenherkunft
' End If
'wichtig dass count - Paratemer der Replace Funktion gesetzt wird !
'jedoch wird Schleife dadurch deutlich langsamer
txtAllComstat = Replace(txtAllComstat, ctl.Value & vbCrLf, "", 1, 1)
End If
Next
Next
Loop



So, nun existieren ja in den Kombis bereits Zeilen. Wenn die Schleife das Steuerelement mit der Marke 2 bspw. gefunden hat und die erste Zeile des großen Textfeldes dort hineingeschrieben hat, gibt es keinerlei Kontrolle, ob dieser Wert dort überhaupt schon vorkommt (in der Tabelle also vorhanden ist).
Access geht darüber einfach hinweg. Mache ich das ganze manuell, schreibe also einen Wert in das Kombifeld, der noch nicht existent ist, merkt Access das und ich kann über das Ereignis NotInList darauf reagieren und den Datensatz automatisch hinzufügen lassen.

Eben das geht jedoch nicht, wenn ich die Werte via VBA in das Kombifeld schreibe, das war mein ursprüngliches Anliegen.

Anne Berg
21.01.2008, 10:20
Hallo und danke für deine Erläuterungen.
Ich hatte nur das Ereignis "cboVereinComDierkstat_NotInList" gesehen, aber nirgendwo eine Zuweisung zu dem Feld. Da du ja anfangs schon sagtest, dass das Ereignis (verständlicherweise!) nicht ausgelöst würde, hatte ich erwartet, dass du das doch irgendwie getestet haben müsstest...

flew
21.01.2008, 10:27
Habe ich ja getestet,
in der Tabelle tblVerein fehlt ein Verein (Werder Bremen).

Wenn man also den Lösch-Button klickt, sodass alle Felder leer sind und dann in der Kombifeld für den Verein 'Werder Bremen' einträgt, anschließend das Feld verläßt ist der Datensatz hinzugefügt.
Während der Schleife wird in das Kombifeld ja allerdings auch ein paar Mal der Wert 'Werder Bremen' eingefügt, allerdings ohne am Ende diesen Datensatz hinzugefügt zu haben.

Was mich dann zu der Frage brachte, wie man dieses Ereignis automatisch auslösen könne, einen Code dahinter gibt es ja und er funktioniert auch.
Also werde ich das mit der Funktionslösung probieren.

Gruß

Josef P.
21.01.2008, 10:33
Was mich dann zu der Frage brachte, wie man dieses Ereignis automatisch auslösen könne...
... und die ich bereits in #8 beantwortete. ;)

Aber noch einmal zur Wiederholung, da dies sehr gerne verwechselt wird.
Die Sub "cboVereinComDierkstat_NotInList" ist nicht das Ereignis. Diese Prozedur löst auch nicht das Ereignis aus, sondern sie reagiert auf das eingetretene Ereignis "NotInList" des Steuerelements "cboVereinComDierkstat".

Ein Beispiel-Code wie ein Ereignis ausgelöst wird.

Klasse A:
Public Event EinEreignis()

Public Sub MachEtwas()
'...
'Ereignis auslösen:
RaiseEvent EinEreignis

End Sub

Klasse B:
Private WithEvents objA As A

Public Sub MachEtwas()
Set objA = New A
objA.MachEtwas
End Sub

Private Sub objA_EinEreignis()
MsgBox "Ereignis wurde ausgelöst"
End Sub
A ist eine Klasse, die ein Ereignis auslösen kann.
B reagiert auf das Ereignis "EinEreignis" mit der Sub "objA_EinEreignis".