VBA Bearbeitungsprioritäten

Hallo,

zunächst allen dieses Brettes ein gutes neues Jahr.

meine Frage: gibt es bei VBA eine Priorität in der Abarbeit von Anweisungen? Ich habe folgenden Fall (Excel 2010 auf Laptop, i5-Prozessor):

in meiner Userform gibt es einige Eingabefelder (Textbox1, Textbox2, usw) sowie 2 Label, die ich rot färbe, während die Eingabedaten durch ein Makro geprüft werden, ob sie bereits existieren:

Private Sub CommandButton1_Click()
Label37.Visible = True
Label38.Visible = True
Call Annahme 'DATEN IN DATENBANK EINTRAGEN

Label37 und 38 sind rote Felder, die auf visible=false gesetzt sind. Beim Start dieser Private Sub werden sie sichtbar.

In der Routine „Annahme“ wird die Datenbank (Excel Tabellenblatt) auf 3 Übereinstimmungen (innerhalb 1 Zeile) mit den vorliegenden Eingabedaten geprüft. Es liegen ca. 5400 Zeilen vor.

Nun passiert aber folgendes: Ich starte die Routine „Private Sub CommandButton1_Click()“ per Klick auf den Button, die genannten Label werden nicht rot angezeigt, die Windows Eieruhr läuft zum Zeichen, daß der Rechner arbeitet, nach ca 3 sec. ist die Routine „Annahme“ abgearbeitet (was z.B. zu erkennen ist durch die Meldung „Eingabedaten liegen bereits vor“), dann erst leuchten die beiden roten Felder Label 37 und 38 rot.

Läßt sich diese zeitliche Verschiebung zwischen Anweisung und Wirkung beeinflussen?

Gruß
Pauli

Hallo!
Das, was Du schilderst, ist ganz normales Verhalten unter Windows.
Dort werden bekanntermaßen alle Aktionen über Nachrichten behandelt, die zwischen Fenstern und/oder Threads hin- und hergeschickt werden.
Die Oberfläche einer Anwendung wird dabei im sog. „UI-Thread“ behandelt, davon gibt es pro Anwendung genau einen.
In diesem Thread wird sowohl das Knöpfchendrücken als auch das Neuzeichnen behandelt. Da der UI-Thread mit Deiner CommandButton1_Click-Prozedur beschäftigt ist, kann das Neuzeichnen so lange nicht ausgeführt werden, wie Deine Prozedur läuft.

Die korrekte Lösung dafür wäre, die lang laufende Aktion in einen separaten Thread auszulagern, aber meines Wissens nach unterstützt VBA kein Multithreading.
Dafür gibt es aber das Kommando „DoEvents“, mit dem die Abarbeitung der wartenden Windows-Nachrichten erzwungen werden kann. Wenn Du das nach dem Ändern der Labels aufrufst, müssten sie sofort neu gezeichnet werden (und dann im neuen Zustand sichtbar sein, während das Makro weiter läuft).

Aber ACHTUNG! Die Verwendung von DoEvents kann unerwartete Effekte nach sich ziehen, wenn man nicht so genau weiß, was unter der Haube passiert, daher immer Vorsicht bei der Verwendung!

Gruß,
Martin

Hallo Pauli,

meine Frage: gibt es bei VBA eine Priorität in der Abarbeit
von Anweisungen?

von oben nach unten, mal von Schleifen, Sprüngen usw. abgesehen.

ist die Routine „Annahme“ abgearbeitet (was z.B. zu
erkennen ist durch die Meldung „Eingabedaten liegen bereits
vor“), dann erst leuchten die beiden roten Felder Label 37 und
38 rot.
Läßt sich diese zeitliche Verschiebung zwischen Anweisung und
Wirkung beeinflussen?

Mir fällt da als einziges DoEvents ein, bin aber nicht überzeugt daß
es hilft.
Ich habe es nachgebaut, auch mit Call einer Prozedur die paar
Sekunden läuft, der davor stehende Label-sichtbar Befehl wird vorher
ausgeführt.

Kannst du die Mappe hochladen mit dem obersten Hochladlink in FAQ:2606

Gruß
Reinhard

Die Oberfläche einer Anwendung wird dabei im sog. „UI-Thread“
behandelt, davon gibt es pro Anwendung genau einen.
In diesem Thread wird sowohl das Knöpfchendrücken als auch das
Neuzeichnen behandelt. Da der UI-Thread mit Deiner
CommandButton1_Click-Prozedur beschäftigt ist, kann das
Neuzeichnen so lange nicht ausgeführt werden, wie Deine
Prozedur läuft.

Hallo Martin,

dem kann ich mich in diesem Fall nicht anschließen.
In der folgenden Mappe ist der derzeigt unsichtbare Label1 über
Tabelle1!A1. Drücke mal auf den Button vom Blatt1.
Der Label wird sichtbar bevor da die Unterprodezur abgearbeitet ist.
http://www.uploadagent.de/show-189210-1357134694.html
Nachfolgend die benutzten Codes.

Gruß
Reinhard

in Tabelle1:

Private Sub CommandButton1_Click()
Label1.Visible = True
Call Annahme 'DATEN IN DATENBANK EINTRAGEN
End Sub

in Modul1:

Sub Annahme()
'Application.ScreenUpdating = False
Dim N
For N = 1 To 600000000
Next N
MsgBox „Huhu“
'Application.ScreenUpdating = True
End Sub

Sub Wech()
Tabelle1.Label1.Visible = False
'Application.Run „Tabelle1.CommandButton1_Click“
End Sub

Hallo Reinhard und Martin,

danke für eure Antworten. Ich habe mal die Datei komplett so wie sie ist hochgeladen unter
http://www.file-upload.net/download-7003232/Musikpro…

Ich hoffe es richtig gemacht zu haben.
Bei der Datei läßt sich der Effekt am besten sehen, wenn folgendes gemacht wird:
* im Menü links „Neueingabe“ drücken,
* in der Eingabemaske unten die Taste „Vorlage laden“ drücken (diese Vorlage sind die zuletzt eingegebenen Daten)
* Dann die Taste daneben „Annehmen und weiter eingeben“ drücken (damit werden die gleichen Daten nochmal eingegeben, was vom Programm gemeldet wird).
Mit der Meldung kommen gleichzeitig die beiden Label (rot), die eigentlich hätten sofort kommen sollen.

Danke für eure Mühe
Gruß
Pauli

Grüezi Pauli

danke für eure Antworten. Ich habe mal die Datei komplett so
wie sie ist hochgeladen unter
http://www.file-upload.net/download-7003232/Musikpro…

* im Menü links „Neueingabe“ drücken,
* in der Eingabemaske unten die Taste „Vorlage laden“ drücken
(diese Vorlage sind die zuletzt eingegebenen Daten)
* Dann die Taste daneben „Annehmen und weiter eingeben“
drücken (damit werden die gleichen Daten nochmal eingegeben,
was vom Programm gemeldet wird).
Mit der Meldung kommen gleichzeitig die beiden Label (rot),
die eigentlich hätten sofort kommen sollen.

Das schon genannte ‚DoEvents‘ sollte hier helfen - ich habe dieses mal hier eingefügt und damit erreicht, dass die Labels unmittelbar nach dem Klick auf den Button angezeigt werden:

Private Sub CommandButton1\_Click()
 If Label32.BackColor = &HFF& Then Exit Sub
 Label37.Visible = True
 Label38.Visible = True
**DoEvents**
 Call Annahme 'DATEN IN DATENBANK EINTRAGEN

Mit freundlichen Grüssen

Thomas Ramel

  • MVP für MS-Excel -
1 Like

Hallo!

Kannst mir schon glauben, meine Aussagen sind das Ergebnis jahrelanger Erfahrung :smile:

Mach’ z.B. mal folgendes:
CommandButton und ein Label in der Tabelle und dann im Click des Buttons:

Label1.ForeColor = ColorConstants.vbRed
Application.Wait Now + TimeValue("0:00:03")
Label1.ForeColor = ColorConstants.vbBlack

Ergebnis: Wartecursor für 3 Sekunden und man sieht nicht, dass sich die Farbe ändert.

Nur durch DoEvents vor dem Application.Wait wird die Nachricht zum Neuzeichnen des Labels verarbeitet, bevor sich die Application für 3 Sekunden schlafen legt (und keine Nachrichten verarbeitet).

Aus irgendwelchen Gründen (Interna von Office/VBA) reicht an dieser Stelle (zumindest bei mir) ein DoEvents nicht aus, aber mit 2 funktioniert’s:

Label1.ForeColor = ColorConstants.vbRed
DoEvents
DoEvents
Application.Wait Now + TimeValue("0:00:03")
Label1.ForeColor = ColorConstants.vbBlack

Gruß,
Martin

http://www.file-upload.net/download-7003232/Musikpro…
Mit der Meldung kommen gleichzeitig die beiden Label (rot),
die eigentlich hätten sofort kommen sollen.

Moin Pauli,

das mit DoEvents ist ja geklärt.

Ich habe mal die nachfolgende Funktion von dir in Modul1 getunt,
sie ist jetzt schneller.

Beachte meine Variablendeklarierung, deine ist nicht korrekt, bei
Dim S4 , S5 , S6 As String
sind S4 und S5 Variant.

Das veränderst im Workbook_Open-Code Bildschirmeinstellungen.
Merke dir vorm Ändern die Werte in globalen Variablen oder in
Zellen.
Im Workbook_BeforeClose kannst du dann wieder die Änderungen wieder
zurückändern.

Benutze in deinem Interesse sprechende Namen für Variablen, Elemente
auf den Userformen.
Also z.B. nicht CommandButton4 sondern
cmbVorlage_Laden
oder
cmbLaden
oder
cmbVorlage
o.ä.

Gruß
Reinhard

Function Wiederholungssuche() As Boolean
Dim S4 As String, S5 As String, S6 As String
k = Cells(2, 3).Value + 1 'letzte Zeilennummer der Datenbank
With Sheets(2)
 .Range("M2:M" & k).Formula = "=B2&C2&D2"
 S4 = Eingabe.TextBox2.Text 'Titel aus Eingabefeld
 S5 = Eingabe.TextBox3.Text 'Haupttitel aus Eingabefeld
 S6 = Eingabe.TextBox4.Text 'Band/Interpret aus Eingabefeld
 If Application.WorksheetFunction.CountIf(.Range("M2:M" & k), S4 & S5 & S6) Then
 i2 = Application.WorksheetFunction.Match(S4 & S5 & S6, .Range("M2:M" & k), 0)
 Wiederholungssuche = True
 Infofenster.Label38.Caption = i2
 End If
 .Range("M2:M" & k).ClearContents
End With
End Function
1 Like

…herzlichen Dank für eure Hilfen, an praktischen Beispielen lerne ich am meisten.
Gruß
Pauli

Hallo Reinhard,

ich habe Deinen Verbesserungsvorschlag eingebaut. Donnerwetter, die Routine ist ja so schnell geworden, daß die rote Kennung „in Arbeit“ überflüssig ist. Die habe ich ja nur eingebaut, um zu sehen, ob das Programm abgestürzt ist oder arbeitet.
Erlaube mir dennoch eine Frage:
Die Anweisung „If Application.WorksheetFunction.CountIf(.Range(„M2:M“ & k), S4 & S5 & S6)“ verstehe ich nicht. Die Excel-Hilfe sagt, daß CountIf die Anzahl der Zellen im Bereich… ermittelt. Der Rückgabewert sei Double. Bei if müßte doch true oder false als Rückgabewert stehen.
„Range“ beschreibt den Bereich, „S4&S5&S6“ das Kriterium. Ich verstehe die Frage so: Ist das Kriterium im Bereich vorhanden? (ja oder nein, also true oder false).
Die weitere Anweisung mit „Match“ sagt dann aus, WO das Kriterium erfüllt ist.
Sehe ich das richtig?

Mit dem Einfügen des DoEvents klappt das mit der Anzeige, die lasse ich jetzt aber bei der Schnelligkeit dieser Wiederholungssuche weg.

Nochmals vielen Dank
Pauli

GHrüezi Pauli

Die Anweisung „If
Application.WorksheetFunction.CountIf(.Range(„M2:M“ & k), S4 &
S5 & S6)“ verstehe ich nicht. Die Excel-Hilfe sagt, daß
CountIf die Anzahl der Zellen im Bereich… ermittelt. Der
Rückgabewert sei Double.

Ja, das ist korrekt - eine ‚0‘ wenn keine Übereinstimmung gefunden wurde und eine Zahl die angibt wie oft eine Übereinstimmung gefunden wurde.

Bei if müßte doch true oder false als Rückgabewert stehen.

Jein - eine ‚0‘ wird als False ausgewertet, alles andere als ‚True‘, daher klappt das so wie von Reinhard formuliert.

„Range“ beschreibt den Bereich, „S4&S5&S6“ das Kriterium. Ich
verstehe die Frage so: Ist das Kriterium im Bereich vorhanden?

In Wirklichkeit eben wie oft es vorhanden ist, was dann mit der Prüfung auf True/False darauf hinaus läuft:

(ja oder nein, also true oder false).

Mit freundlichen Grüssen

Thomas Ramel

  • MVP für MS-Excel -

Hallo Pauli,

Donnerwetter, die Routine ist ja so schnell geworden, daß die
rote Kennung „in Arbeit“ überflüssig ist.

ja, denn die eingebauten Funktionen sind zigmal (1000?) schneller als
Vba. Vba ist ein Interpreter, bis auf wenig Kompliertes am Anfang
wird zur Laufzeit jede Anweisung interpretiert, das kostet Zeit.
Und paar tausend Zellen abzuklappern per Code, dann kannste
gelegentlich lange Kaffee trinken gehen in der Zeit :smile:

Was zum Glück sehr sehr schnell geht ist per Vba Formeln wie hier
in M2:Mx einzufügen.

Die habe ich ja nur
eingebaut, um zu sehen, ob das Programm abgestürzt ist oder
arbeitet.

Kann man so machen. Je nach Benutzer bzw. nur für einen selbst
reicht auch in der früheren Wiederholungsfunktion
in die i-Schleife anfangs einzubauen
Application.StatusBar = "Bearbeite Zeile " & i & " / " & k

Unterhalb von Next i dann
Application.StatusBar = False
dann erscheint wieder das gewohnte „Bereit“ dort.

Die Excel-Hilfe sagt, daß
CountIf die Anzahl der Zellen im Bereich… ermittelt. Der
Rückgabewert sei Double. Bei if müßte doch true oder false als
Rückgabewert stehen.

Jain, Excel denkt da mit. Ich hörte per Gerücht es soll „richtige“
Programmiersprachen geben wo es in der Tat einen Fehler an dieser
Stelle gegeben hätte, richtig erkannt von dir.

Für If ist aber jeder Zahlenwert AUßER Null True, nur 0 ist False.
Auch ein datum was in String-Form vorliegt.
Nachfolgend ein Testcode, ersetze mal da die
If „1.1.12“ Then, durch eines der folgenden:
0, False, 6, -10, 0.1, „abc“, und was dir einfällt.
Alles was als Zahl gedeutet werden kann von Excel wird als
boolean angesehen und wie oben erläutert ist das dann True/False.

Oder teste das mit
MsgBox CBool(„1.1.12“)

„Range“ beschreibt den Bereich, „S4&S5&S6“ das Kriterium. Ich
verstehe die Frage so: Ist das Kriterium im Bereich vorhanden?
(ja oder nein, also true oder false).

Jain, CountiF liefert schon eine Zahl. IF/Excel macht daraus
True/False.

Die weitere Anweisung mit „Match“ sagt dann aus, WO das
Kriterium erfüllt ist.
Sehe ich das richtig?

Ja, Match alleine gibt einen Fehler wenn das Kriterium nicht
gefunden wird. Um diesen Fehler zu vermeiden habe ich mir
angewöhnt vorher mit Countif nach dem Vorhandensein zu forschen.
Zumindest bis XL 2003 war das so. Ab XL 2007 könnte man da
WennFehler (in Englisch) einbauen.

Gruß
Reinhard

Sub tt()
If „1.1.12“ Then
MsgBox „richtig“
Else
MsgBox „falsch“
End If
End Sub

Hallo Martin,

Kannst mir schon glauben, meine Aussagen sind das Ergebnis
jahrelanger Erfahrung :smile:

ich glaub dir schon daß dies so ist.
Reinhard ist aber nur mein Zweitnick, mein richtiger Nick ist
ungläubiger Thomas :smile:
Insofern, was ist mit meiner Beispielmappe? Da braucht man kein DoEvents. Sowas bringt mich ins Grübeln :frowning:

Mach’ z.B. mal folgendes:
Ergebnis: Wartecursor für 3 Sekunden und man sieht nicht, dass
sich die Farbe ändert.

Ja, habe ich gemacht und viel verschiedenes ausprobiert um da gleich
rot zu sehen. Hat mit keiner Idee geklappt.

Aus irgendwelchen Gründen (Interna von Office/VBA) reicht an
dieser Stelle (zumindest bei mir) ein DoEvents nicht aus, aber
mit 2 funktioniert’s:
DoEvents
DoEvents

Stimmt auch. Ich sah auch schon Code wo zweimal nacheiander abgefragt
wird ob der mit Createobject gestartete IE noch buzy ist oder nicht.
Okay, okay, kann auch andere Gründe sein.

Nochmal zu meiner Beispielmappe, haste sie probiert, wenn ja, warum
klappt es da?

Gruß
REinhard

Hallo nochmal!

Ich hatte Deine Mappe bislang noch nicht ausprobiert, aber jetzt eben.
Excel/VBA hat anscheinend irgendwo versteckt/intern noch Aufrufe zum Neuzeichnen beim Ändern bestimmter Properties der Steuerelemente.

Das Setzen von Visible scheint das Steuerelement sofort zu aktualisieren.
Das Setzen der Vordergrundfarbe nicht.

Wenn Du nach dem Label1.Visible = True noch Label1.ForeColor = vbRed einbaust, siehst Du zwar das Label, aber mit schwarzer Schrift.

Komischerweise passiert das Gleiche, wenn Du erst die Farbe auf rot setzt und das Label danach sichtbar machst, also scheint irgendwo in den Untiefen von VBA noch irgendwas - sagen wir mal - merkwürdig zu laufen.

Mit der Reihenfolge Visible, ForeColor + 2* DoEvents klappt es aber auch hier.

Gruß,
Martin

Hallo Martin,

Excel/VBA hat anscheinend irgendwo versteckt/intern noch
Aufrufe zum Neuzeichnen beim Ändern bestimmter Properties der
Steuerelemente.
Das Setzen von Visible scheint das Steuerelement sofort zu
aktualisieren.
Das Setzen der Vordergrundfarbe nicht.

danke für deine Nachschau. Jetzt ist dir klar warum ich skeptisch bei
deiner Aussage war da ich es ja grade wie gezeigt getestet hatte.
Jetzt nach dieser Antwort von dir ist alles okay, wir schieben alles
auf Excel *lach*

Was bleibt einem auch übrig, wenn da Excel das was ich als Mitdenken
bezeichne macht und man Excels intere Regeln dafür nicht kennt?
Mit Mitdenke meine ich es so, mal denkt Excel es sich so, mal halt
anders.
Bei Visible so, bei Forecolor anders.

Ich bin mir sehr sicher wenn man alle Eigenschaften so eines Labels
durchtestet auf das Verhalten in diesem Fall, eine klare Linie wird
da zumindest für mein Wissen nicht erkennbar sein.

scheint irgendwo in den Untiefen von VBA noch irgendwas -
sagen wir mal - merkwürdig zu laufen.

Jepp, dein merkwürdig ist mein Mitdenken.

Gruß
Reinhard